Le MakeFile parfait pour Symfony (au moins pour moi ! π)
PubliΓ© le 30/11/2018 • ActualisΓ© le 10/10/2021
J'ai toujours utilisΓ© des scripts shell (.sh
) pour simplifier mes tΓ’ches de dΓ©veloppement pour mes projets Symfony. Mais j'ai rΓ©cemment dΓ©couvert (ici
et ici)
qu'il Γ©tait possible d'utiliser un Makefile. C'est vraiment beaucoup plus pratique que les scripts shell pour les raisons suivantes :
- Γa permet de documenter toutes les tΓ’ches de dΓ©veloppement dans un seul fichier.
- On profite de l'autocomplΓ©tion des tΓ’ches puisque Make est un outil Unix standard.
- Les erreurs sont interceptΓ©es et la tΓ’che principale est arrΓͺtΓ©e dΓ¨s qu'une erreur est dΓ©tectΓ©e dans une des sous tΓ’ches.
Le fichier Makefile que vous voyez ci dessous est celui qui est actuellement utilisΓ© pour ce projet Strangebuzz.com. Vous pouvez le tester pour voir la sortie par dΓ©faut de l'appel de la commande make
. Cette sortie est la tΓ’che par dΓ©faut "aide" (help), qui liste toutes les tΓ’ches pouvant Γͺtre appelΓ©es. N'oubliez pas de changer le paramΓ¨tre PROJECT
en haut du fichier π. Le joli affichage de la sortie ANSI est fait Γ l'aide de de la librairie sensiolabs/ansi-to-html
.
[Maj 21/04/2020] Ajout de cibles pour Docker (build, sh, logs).
[Maj 21/04/2020] Ajout des cibles pour Yarn / Webpack.
[Maj 07/03/2020] Affichage de la sortie console telle qu'elle apparait dans le terminal.
[BONUS] Le code PHP permettant de récupérer la sortie de l'appel système grÒce au composant Symfony Process et à la librairie ansi-to-html.
# ββ Inspired by βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# http://fabien.potencier.org/symfony4-best-practices.html
# https://speakerdeck.com/mykiwi/outils-pour-ameliorer-la-vie-des-developpeurs-symfony?slide=47
# https://blog.theodo.fr/2018/05/why-you-need-a-makefile-on-your-project/
# Setup ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Parameters
SHELL = sh
PROJECT = strangebuzz
GIT_AUTHOR = COil
HTTP_PORT = 8000
# Executables
EXEC_PHP = php
COMPOSER = composer
REDIS = redis-cli
GIT = git
YARN = yarn
NPX = npx
# Alias
SYMFONY = $(EXEC_PHP) bin/console
# if you use Docker you can replace with: "docker-compose exec my_php_container $(EXEC_PHP) bin/console"
# Executables: vendors
PHPUNIT = ./vendor/bin/phpunit
PHPSTAN = ./vendor/bin/phpstan
PHP_CS_FIXER = ./vendor/bin/php-cs-fixer
PHPMETRICS = ./vendor/bin/phpmetrics
# Executables: local only
SYMFONY_BIN = symfony
BREW = brew
DOCKER = docker
DOCKER_COMP = docker compose
# Executables: prod only
CERTBOT = certbot
# Misc
.DEFAULT_GOAL = help
.PHONY : # Not needed here, but you can put your all your targets to be sure
# there is no name conflict between your files and your targets.
## ββ π The Strangebuzz Symfony Makefile π βββββββββββββββββββββββββββββββββββ
help: ## Outputs this help screen
@grep -E '(^[a-zA-Z0-9_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/'
## ββ Composer π§ββοΈ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
install: composer.lock ## Install vendors according to the current composer.lock file
@$(COMPOSER) install --no-progress --prefer-dist --optimize-autoloader
## ββ PHP π (macOS with brew) βββββββββββββββββββββββββββββββββββββββββββββββββ
php-upgrade: ## Upgrade PHP to the last version
@$(BREW) upgrade php
php-set-8-2: ## Set php 8.2 as the current PHP version
@$(BREW) unlink php
@$(BREW) link --overwrite php@8.2
php-set-8-3: ## Set php 8.3 as the current PHP version
@$(BREW) unlink php
@$(BREW) link --overwrite php@8.3
php-set-8-1: ## Set php 8.1 as the current PHP version
@$(BREW) unlink php
@$(BREW) link --overwrite php@8.1
# Snippet L53+14 in _127.html.twig
## ββ Symfony π΅ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
sf: ## List all Symfony commands
@$(SYMFONY)
cc: ## Clear the cache. DID YOU CLEAR YOUR CACHE????
@$(SYMFONY) c:c
warmup: ## Warmup the cache
@$(SYMFONY) cache:warmup
fix-perms: ## Fix permissions of all var files
@chmod -R 777 var/*
assets: purge ## Install the assets with symlinks in the public folder
@$(SYMFONY) assets:install public/ # Don't use "--symlink --relative" with a Docker env
purge: ## Purge cache and logs
@rm -rf var/cache/* var/logs/*
## ββ Symfony binary π» ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
cert-install: ## Install the local HTTPS certificates
@$(SYMFONY_BIN) server:ca:install
serve: ## Serve the application with HTTPS support (add "--no-tls" to disable https)
@$(SYMFONY_BIN) serve --daemon --port=$(HTTP_PORT)
unserve: ## Stop the webserver
@$(SYMFONY_BIN) server:stop
# Snippet L90+8 => templates/blog/posts/_64.html.twig
## ββ elasticsearch π βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
populate: ## Reset and populate the Elasticsearch index
#@$(SYMFONY) fos:elastica:reset
#@$(SYMFONY) fos:elastica:populate --index=app
@$(SYMFONY) strangebuzz:index-articles
# Snippet L102+4 => templates/blog/posts/_51.html.twig
list-index: ## List all indexes on the cluster
@curl http://localhost:9209/_cat/indices?v
delete-index: ## Delete a given index (parameters: index=app_2021-01-05-075600")
@curl -X DELETE "localhost:9209/$(index)?pretty"
## ββ Docker π³ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
up: ## Start the docker hub (PHP,caddy,MySQL,redis,adminer,elasticsearch)
$(DOCKER_COMP) up --detach
build: ## Builds the images (php + caddy)
$(DOCKER_COMP) build --pull --no-cache
down: ## Stop the docker hub
$(DOCKER_COMP) down --remove-orphans
check: ## Docker check
@$(DOCKER) info > /dev/null 2>&1 # Docker is up
@test '"healthy"' = `$(DOCKER) inspect --format "{{json .State.Health.Status }}" strangebuzz-db-1` # Db container is up and healthy
# Snippet L126+2 => templates/snippet/code/_135.html.twig
sh: ## Log to the docker container
@$(DOCKER_COMP) exec php sh
logs: ## Show live logs
@$(DOCKER_COMP) logs --tail=0 --follow
wait-for-mysql: ## Wait for MySQL to be ready
@bin/wait-for-mysql.sh
wait-for-elasticsearch: ## Wait for Elasticsearch to be ready
@bin/wait-for-elasticsearch.sh
bash: ## Connect to the application container
@$(DOCKER) container exec -it php bash
## ββ Project π βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
start: up wait-for-mysql load-fixtures populate serve ## Start docker, load fixtures, populate the Elasticsearch index and start the webserver
reload: load-fixtures populate ## Load fixtures and repopulate the Meilisearch index
stop: down unserve ## Stop docker and the Symfony binary server
cc-redis: ## Flush all Redis cache
@$(REDIS) -p 6389 flushall
commands: ## Display all commands in the project namespace
@$(SYMFONY) list $(PROJECT)
load-fixtures: ## Build the DB, control the schema validity, load fixtures and check the migration status
@$(SYMFONY) doctrine:cache:clear-metadata
@$(SYMFONY) doctrine:database:create --if-not-exists
@$(SYMFONY) doctrine:schema:drop --force
@$(SYMFONY) doctrine:schema:create
@$(SYMFONY) doctrine:schema:validate
@$(SYMFONY) hautelook:fixtures:load --no-interaction
init-snippet: ## Initialize a new snippet
@$(SYMFONY) $(PROJECT):init-snippet
## ββ Tests β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
test: phpunit.xml check ## Run tests with optionnal suite and filter
@$(eval testsuite ?= 'all')
@$(eval filter ?= '.')
@$(PHPUNIT) --testsuite=$(testsuite) --filter=$(filter) --stop-on-failure
# Snippet L171+4 => templates/blog/posts/_123.html.twig + templates/blog/posts/_178.html.twig
test-all: phpunit.xml ## Run all tests
@$(PHPUNIT) --stop-on-failure
## ββ Coding standards β¨ ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
cs: fix-php fix-js stan ## Run all coding standards checks
static-analysis: stan ## Run the static analysis (PHPStan)
stan: ## Run PHPStan
@$(PHPSTAN) analyse -c configuration/phpstan.neon --memory-limit 1G
lint-php: ## Lint files with php-cs-fixer
@$(PHP_CS_FIXER) fix --allow-risky=yes --dry-run --config=php-cs-fixer.php
fix-php: ## Fix files with php-cs-fixer
@PHP_CS_FIXER_IGNORE_ENV=1 $(PHP_CS_FIXER) fix --allow-risky=yes --config=php-cs-fixer.php
## ββ Deploy & Prod π βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
deploy: ## Full no-downtime deployment with EasyDeploy (with pre-deploy Git hooks)
@test -z "`git status --porcelain`" # Prevent deploy if there are modified or added files
@test -z "`git diff --stat --cached origin/master`" # Prevent deploy if there is something to push on master
@$(SYMFONY) deploy -v # Deploy with EasyDeploy
# Snippet L196+4 => templates/snippet/code/_128.html.twig
env-check: ## Check the main ENV variables of the project
@printenv | grep -i app_
le-renew: ## Renew Let's Encrypt HTTPS certificates
@$(CERTBOT) --apache -d strangebuzz.com -d www.strangebuzz.com
## ββ Yarn π± / JavaScript βββββββββββββββββββββββββββββββββββββββββββββββββββββ
dev: ## Rebuild assets for the dev env
@$(YARN) install --check-files
@$(YARN) run encore dev
watch: ## Watch files and build assets when needed for the dev env
@$(YARN) run encore dev --watch
encore: ## Build assets for production
@$(YARN) run encore production
lint-js: ## Lints JS coding standarts
@$(NPX) eslint assets/js
fix-js: ## Fixes JS files
@$(NPX) eslint assets/js --fix
## ββ Stats π βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
stats: ## Commits by the hour for the main author of this project
@$(GIT) log --author="$(GIT_AUTHOR)" --date=iso | perl -nalE 'if (/^Date:\s+[\d-]{10}\s(\d{2})/) { say $$1+0 }' | sort | uniq -c|perl -MList::Util=max -nalE '$$h{$$F[1]} = $$F[0]; }{ $$m = max values %h; foreach (0..23) { $$h{$$_} = 0 if not exists $$h{$$_} } foreach (sort {$$a <=> $$b } keys %h) { say sprintf "%02d - %4d %s", $$_, $$h{$$_}, "*"x ($$h{$$_} / $$m * 50); }'
## ββ JWT πΈ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
BEARER = `cat ./config/jwt/bearer.txt`
BASE_URL = https://127.0.0.1#https://www.strangebuzz.com
PORT = :8000
jwt-generate-keys: ## Generate the main JWT ket set (you can use the "lexik:jwt:generate-keypair" command now)
@mkdir -p config/jwt
@openssl genpkey -out ./config/jwt/private.pem -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096
@openssl pkey -in ./config/jwt/private.pem -out ./config/jwt/public.pem -pubout
jwt-create-ok: ## Create a JWT for a valid test account (you can use the "lexik:jwt:generate-token" command now)
@curl -s POST -H "Content-Type: application/json" ${BASE_URL}${PORT}/api/login_check -d '{"username":"reader","password":"test"}' | jq .token | sed "s/\"//g"
jwt-create-nok: ## Login attempt with wrong credentials
@curl -s POST -H "Content-Type: application/json" ${BASE_URL}${PORT}/api/login_check -d '{"username":"foo","password":"bar"}' | jq
jwt-test: ./config/jwt/bearer.txt ## Test a JWT token to access an API Platform resource
@curl -s GET -H 'Cache-Control: no-cache' -H "Content-Type: application/json" -H "Authorization: Bearer ${BEARER}" ${BASE_URL}${PORT}/api/books/1 | jq
# Snippet L231+17 => templates/blog/posts/_126.html.twig
## ββ Code Quality reports π ββββββββββββββββββββββββββββββββββββββββββββββββββ
report-metrics: ## Run the phpmetrics report
@$(PHPMETRICS) --report-html=var/phpmetrics/ src/
coverage: ## Create the code coverage report with PHPUnit
$(EXEC_PHP) -d xdebug.enable=1 -d xdebug.mode=coverage -d memory_limit=-1 vendor/bin/phpunit --coverage-html=var/coverage
En bonus, le snippet permettant d'utiliser ce code : π<?php
declare(strict_types=1);
namespace App\Controller\Snippet;
use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Process\Process;
/**
* J'utilise un trait PHP afin d'isoler chaque snippet dans un fichier.
* Ce code doit Γͺtre apellΓ© d'un contrΓ΄leur Symfony Γ©tendant AbstractController (depuis Symfony 4.2)
* ou Symfony\Bundle\FrameworkBundle\Controller\Controller (Symfony <= 4.1).
* Les services sont injectΓ©s dans le constructeur du contrΓ΄leur principal.
*
* @property KernelInterface $kernel
*
* @see https://github.com/sensiolabs/ansi-to-html
*/
trait Snippet8Trait
{
public function snippet8(): void
{
$process = new Process([
'make',
'-f',
$this->kernel->getProjectDir().'/Makefile',
]);
$process->run();
echo (new AnsiToHtmlConverter())->convert($process->getOutput()); // That's it! π
}
}
ExΓ©cuter le snippet βͺ this.showUnitTest ? this.trans.hide_unit_test : this.trans.show_unit_test β« Plus sur Stackoverflow Lire la doc Plus sur le web
<?php
declare(strict_types=1);
namespace App\Tests\Integration\Controller\Snippets;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Process\Process;
/**
* @see Snippet
*/
final class Snippet8Test extends KernelTestCase
{
protected function setUp(): void
{
self::bootKernel();
}
/**
* @see Snippet8Trait::snippet8
*/
public function testSnippet8(): void
{
$process = new Process(['make', '-f', self::$kernel->getProjectDir().'/Makefile']);
$process->run();
self::assertStringContainsStringIgnoringCase('The Strangebuzz Symfony Makefile', $process->getOutput());
}
}