Usando ficheros Makefile
Tal y como vimos en el post sobre Uso avanzado de Docker, a veces tenemos que introducir los mismos comandos una y otra vez mientras trabajamos en nuestros proyectos. Veamos cómo ganar agilidad mediante el uso de ficheros Makefile y el comando make.
Introducción
¿Qué es un fichero Makefile?
Un fichero Makefile es un archivo que contiene un conjunto de directivas que permiten automatizar tareas, especialmente durante la fase de desarrollo de software.
Diferencias con otras opciones
En la actualidad podemos hacer uso de múltiples mecanismos para automatizar tareas:
- Creando comando alias en nuestra sesión de usuario
- Manteniendo un repositorio local de ficheros bash con nuestros comandos
- Haciendo uso de Oh! My zsh, una shell altamente personalizable con plugins que abarcan la mayoría de escenarios y tareas tediosas.
- Etc.
Pero este tipo de opciones no dejan de ser soluciones individuales, que dependen de las preferencias de cada usuario y, por tanto, limitadas a la máquina donde se instalan o configuran.
Ahora bien, cuando trabajamos en equipo debemos cambiar el foco y buscar opciones transversales que causen muy poco impacto a cada usuario, que sean fáciles de usar y que no impliquen reconfigurar parcial o total el entorno de cada colaborador. Y es aquí donde los ficheros Makefile entran en juego ya que toda distribución UNIX o Linux viene con el comando make pre-instalado y, por tanto, sólo tenemos que preocuparnos de definir los comandos que vamos a necesitar.
Usando ficheros Makefile
Make es una herramienta de gestión de dependencias usada en entornos UNIX y permite compilar nuestra aplicación o bien, ejecutar comandos de consola de una forma automatizada.
Así pues, make usa el fichero Makefile en el path actual como entrada. Para ver la ayuda de este comando podemos usar el parámetro --help
:
$ make --help
Una ventaja del comando
make
es que permite el autocompletado de comandos de nuestro Makefile
Creando nuestro propio Makefile
Existen algunas secciones específicas de los ficheros Makefile tales como .PHONY y .DEFAULT_GOAL que definen el comportamiento por defecto de nuestro fichero Makefile. Así pues, si no especificamos ningún target mostrará el especificado en .DEFAULT_GOAL.
Algunos consejos rápidos para la creación de ficheros Makefile:
- Por defecto cada comando que se ejecuta muestra su línea de ejecución y parámetros por consola, además del resultado. Si sólo queremos mostrar el resultado bastará con anteponer una
@
al comando. - Para comentar zonas de código o comandos en sí, se usa el carácter almohadilla
#
.
Plantilla genérica
.PHONY: no_targets__ info help build deploy doc
no_targets__:
.DEFAULT_GOAL := help
#-----------------------------------------------------------------
# CONSTANTS
#-----------------------------------------------------------------
ifneq (,$(findstring xterm,${TERM}))
BLACK := $(shell tput -Txterm setaf 0)
RED := $(shell tput -Txterm setaf 1)
GREEN := $(shell tput -Txterm setaf 2)
YELLOW := $(shell tput -Txterm setaf 3)
BLUE := $(shell tput -Txterm setaf 4)
MAGENTA := $(shell tput -Txterm setaf 5)
CYAN := $(shell tput -Txterm setaf 6)
WHITE := $(shell tput -Txterm setaf 7)
RESET := $(shell tput -Txterm sgr0)
else
BLACK := ""
RED := ""
GREEN := ""
YELLOW := ""
BLUE := ""
MAGENTA := ""
CYAN := ""
WHITE := ""
RESET := ""
endif
COMMAND_COLOR := $(YELLOW)
#-----------------------------------------------------------------
# HELP
#-----------------------------------------------------------------
help:
@echo "╔══════════════════════════════════════════════════════════════════════════════╗"
@echo "║ ║"
@echo "║ ${CYAN}.:${RESET} AVAILABLE COMMANDS ${CYAN}:.${RESET} ║"
@echo "║ ║"
@echo "╚══════════════════════════════════════════════════════════════════════════════╝"
@echo ""
@grep -E '^[a-zA-Z_0-9%-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "· ${TARGET_COLOR}%-30s${RESET} %s\n", $$1, $$2}'
@echo ""
#-----------------------------------------------------------------
# COMMANDS
#-----------------------------------------------------------------
priv-1:
@command step #1
@command step #2
...
priv-2:
command step #1
public: priv-1 priv-2 ## Command summary
command step #1
...
El comando
make help
mostrará, de nuestro fichero Makefile, todos los comandos que tengan en su definición un comentario que empiece por dos almohadillas (##
).Esto nos permite tener comandos no expuestos públicamente (comandos privados o auxiliares) y comandos públicos con una breve información descriptiva
Comentarios
Linea | Descripción |
---|---|
.DEFAULT_GOAL := help |
Aquí indicamos el comportamiento por defecto del comando make sin parámetro alguno. Por defecto que muestre la ayuda contextual de los comandos públicos del Makefile |
COMMAND_COLOR := $(YELLOW) |
Indicamos el color que deseamos para el listado de nuestros comandos |
public: priv-1 priv-2 |
Para desarrollar comandos que realicen uno o varios comandos atómicos, se recomienda crear esos comandos atómicos de manera privada y, en el comando principal, añadirlos como dependencia. De este modo se ejecutará, por orden de aparición, los pasos de los diferentes comandos atómicos y, por último, los del comando principal. |
Comandos make frecuentes para aplicaciones PHP
Comando | Descripción |
---|---|
build | Construye el contenedor |
rebuild | Reconstruye el contenedor |
up | Arranca el contenedor |
down | Apaga el contenedor |
restart | Reinicia el contenedor |
logs | Visualiza los logs del contenedor |
info | Visualiza la información de los servicios de la aplicación |
bash | Accedemos mediante bash al contenedor de servicio |
composer-install | Instala las dependencias en entorno local |
composer-install-no-dev | Instala las dependencias en entorno de producción |
composer-update | Actualiza las dependencias de la aplicación |
composer-dump-auto | Actualiza la carga de clases de la aplicación |
tests | Ejecuta los tests de la aplicación |
tests-debug | Ejecuta los tests de la aplicación en modo depuración |
cache-clear | Elimina la cache de nuestra aplicación |
Makefile resultante
.PHONY: no_targets__ info help build deploy doc
no_targets__:
.DEFAULT_GOAL := help
#-----------------------------------------------------------------
# CONSTANTS
#-----------------------------------------------------------------
ifneq (,$(findstring xterm,${TERM}))
BLACK := $(shell tput -Txterm setaf 0)
RED := $(shell tput -Txterm setaf 1)
GREEN := $(shell tput -Txterm setaf 2)
YELLOW := $(shell tput -Txterm setaf 3)
BLUE := $(shell tput -Txterm setaf 4)
MAGENTA := $(shell tput -Txterm setaf 5)
CYAN := $(shell tput -Txterm setaf 6)
WHITE := $(shell tput -Txterm setaf 7)
RESET := $(shell tput -Txterm sgr0)
else
BLACK := ""
RED := ""
GREEN := ""
YELLOW := ""
BLUE := ""
MAGENTA := ""
CYAN := ""
WHITE := ""
RESET := ""
endif
TARGET_COLOR := $(YELLOW)
SERVICE_NAME = api
#-----------------------------------------------------------------
# HELP
#-----------------------------------------------------------------
help:
@echo "╔══════════════════════════════════════════════════════════════════════════════╗"
@echo "║ ║"
@echo "║ ${CYAN}.:${RESET} AVAILABLE COMMANDS ${CYAN}:.${RESET} ║"
@echo "║ ║"
@echo "╚══════════════════════════════════════════════════════════════════════════════╝"
@echo ""
@grep -E '^[a-zA-Z_0-9%-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "· ${TARGET_COLOR}%-30s${RESET} %s\n", $$1, $$2}'
@echo ""
#-----------------------------------------------------------------
# MISCELANEOUS
#-----------------------------------------------------------------
check-is-required-%:
@if [ -z "$($(*))" ] ; then \
echo "" ; \
echo " ⛔ ${RED}Parameter [ ${WHITE}${*}${RED} ] is required!${RESET}" ; \
echo "" ; \
exit 1 ; \
fi;
#-----------------------------------------------------------------
# DOCKER-COMPOSE RELATED
#-----------------------------------------------------------------
build: ## Builds the container(s)
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ BUILDING THE SERVICE(s) │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose build
@echo " 📦 ${GREEN}Build done!${RESET}"
@echo ""
build: ## Builds the container(s)
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ BUILDING THE SERVICE(s) │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose build
@echo " 📦 ${GREEN}Service has been built!${RESET}"
@echo ""
rebuild: down build ## Rebuilds the container(s)
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ REBUILDING THE SERVICE(s) │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose up --remove-orphans
@echo " 📦 ${CYAN}Service has been rebuilt!${RESET}"
@echo ""
up: check-folder ## Starts the container(s) in daemonized mode
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ BUILDING, (RE)CREATING, STARTING AND ATTACHING TO CONTAINERS FOR A SERVICE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose up --remove-orphans -d
@echo ""
@echo " 🟢 ${GREEN}Service is up & running!${RESET}"
@echo ""
down: ## Stops the container(s)
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ STOPPING AND REMOVING CONTAINERS, NETWORKS, VOLUMES AND IMAGES │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose down
@echo ""
@echo " 🟠 ${YELLOW}Service has been stopped!${RESET}"
@echo ""
restart: ## Restarts the container(s)
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ RESTARTING ALL STOPPED AND RUNNING SERVICES │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose restart
@echo ""
@echo " 🔵 ${BLUE}Service has been restarted!${RESET}"
@echo ""
logs: ## Prints out the container(s) log(s)
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ DISPLAYING LOG OUTPUT FROM SERVICES │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose logs
@echo ""
@echo " 🔎 ${WHITE}Service log were displayed!${RESET}"
@echo ""
#-----------------------------------------------------------------
# SERVICE RELATED
#-----------------------------------------------------------------
bash: ## Opens a bash connection to main service
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ GETTING AN INTERACTIVE PROMPT FROM SERVICE CONTAINER │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash
@echo ""
@echo " ✅ ${GREEN}Task is done!${RESET}"
@echo ""
info: ## Displays information about main service
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ DISPLAYING GENERIC INFORMATION FROM SERVICE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash -c "php --version"
@echo ""
@echo " ✅ ${GREEN}Task is done!${RESET}"
@echo ""
#-----------------------------------------------------------------
# COMPOSER RELATED
#-----------------------------------------------------------------
composer-install-no-dev: ## Performs <composer install --optimize-autoloader --no-dev> onto main service
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ INSTALLING DEPENDENCIES ONTO APPLICATION SERVICE IN PRODUCTION MODE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash -c "COMPOSER_MEMORY_LIMIT=-1 composer install --optimize-autoloader --no-dev"
@echo ""
composer-install: ## Performs <composer install --optimize-autoloader> onto main service
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ INSTALLING DEPENDENCIES ONTO APPLICATION SERVICE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash -c "COMPOSER_MEMORY_LIMIT=-1 composer install --optimize-autoloader"
@echo ""
@echo " ✅ ${GREEN}Task is done!${RESET}"
@echo ""
composer-update: ## Performs <composer update> onto main service
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ UPDATING DEPENDENCIES ONTO APPLICATION SERVICE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash -c "COMPOSER_MEMORY_LIMIT=-1 composer update"
@echo ""
@echo " ✅ ${GREEN}Task is done!${RESET}"
@echo ""
composer-dump-auto: ## Performs <composer dump-auto> onto main service
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ GENERATING OPTIMIZED AUTOLOAD CLASS MAP ONTO APPLICATION SERVICE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash -c "COMPOSER_MEMORY_LIMIT=-1 composer dump-auto"
@echo ""
@echo " ✅ ${GREEN}Task is done!${RESET}"
@echo ""
#-----------------------------------------------------------------
# APPLICATION RELATED
#-----------------------------------------------------------------
tests: ## Executes the PHPUnit testsuite onto main service
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ RUNNING THE TESTSUITE ONTO APPLICATION SERVICE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash -c "cd /code/api && ./vendor/bin/phpunit"
@echo ""
@echo " ✅ ${GREEN}Task is done!${RESET}"
@echo ""
tests-debug: ## Executes the PHPUnit testsuite with debug flag
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ RUNNING THE TESTSUITE ONTO APPLICATION SERVICE IN DEBUG MODE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash -c "cd /code/api && ./vendor/bin/phpunit --debug"
@echo ""
@echo " ✅ ${GREEN}Task is done!${RESET}"
@echo ""
cache-clear: ## Performs <php artisan clear:cache> onto app container
@echo "┌─────────────────────────────────────────────────────────────────────────────┐"
@echo "│ CLEARING THE APPLICATION CACHE FROM APPLICATION SERVICE │"
@echo "└─────────────────────────────────────────────────────────────────────────────┘"
@echo ""
@docker-compose exec ${SERVICE_NAME} bash -c "cd /code/api && php artisan cache:clear"
@echo ""
@echo " ✅ ${GREEN}Task is done!${RESET}"
@echo ""
Descarga
Puedes descargar el fichero de ejemplo desde aquí.
Versión del documento
[^v1.0]: Última Modificación: 03/03/2021