Le MakeFile parfait pour Symfony (au moins pour moi ! 😁)

Publié le 30/11/2018 • Mis à jour le 19/12/2019

English language detected! 🇬🇧

  We noticed that your browser is using English. Do you want to read this post in this language?

Read the english version 🇬🇧

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. 😉

[Maj 19/12/2019] Entrées pour changer de version de PHP avec brew.
[Maj 26/11/2019] Ajouté des entrées pour le binaire Symfony.
[BONUS] Le code PHP permettant de récupérer la sortie de l'appel système grâce au composant Symfony Process.


# —— 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 ————————————————————————————————————————————————————————————————————————
SHELL         = bash
PROJECT       = strangebuzz
EXEC_PHP      = php
REDIS         = redis-cli
GIT           = git
GIT_AUTHOR    = COil
SYMFONY       = $(EXEC_PHP) bin/console
SYMFONY_BIN   = ./symfony
COMPOSER      = $(EXEC_PHP) composer.phar
DOCKER        = docker-compose
BREW          = brew
.DEFAULT_GOAL = help
#.PHONY       = # Not needed for now

## —— 🐝 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/'

wait: ## Sleep 5 seconds
	sleep 5

## —— Composer 🧙‍♂️ ————————————————————————————————————————————————————————————
install: composer.lock ## Install vendors according to the current composer.lock file
	$(COMPOSER) install

update: composer.json ## Update vendors according to the composer.json file
	$(COMPOSER) update

## —— PHP 🐘 (macOS with brew) —————————————————————————————————————————————————
php-upgrade: ## Upgrade PHP to the last version
	$(BREW) upgrade php

php-set-7-2: ## Set php 7.2 as the current PHP version
	$(BREW) unlink php@7.3 && brew unlink php@7.4
	$(BREW) link php@7.2 --force

php-set-7-3: ## Set php 7.3 as the current PHP version
	$(BREW) unlink php@7.2 && brew unlink php@7.4
	$(BREW) link php@7.3 --force

php-set-7-4: ## Set php 7.4 as the current PHP version
	$(BREW) unlink php@7.2 && brew unlink php@7.3
	$(BREW) link php@7.4 --force

## —— Symfony 🎵 ———————————————————————————————————————————————————————————————
sf: ## List all Symfony commands
	$(SYMFONY)

cc: ## Clear the cache. DID YOU CLEAR YOUR CACHE????
	$(SYMFONY) c:c

warmup: ## Warmump 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/ --symlink --relative

purge: ## Purge cache and logs
	rm -rf var/cache/* var/logs/*

## —— Symfony binary 💻 ————————————————————————————————————————————————————————
bin-install: ## Download and install the binary in the project (file is ignored)
	curl -sS https://get.symfony.com/cli/installer | bash
	mv ~/.symfony/bin/symfony .

cert-install: symfony ## Install the local HTTPS certificates
	$(SYMFONY_BIN) server:ca:install

serve: symfony ## Serve the application with HTTPS support
	$(SYMFONY_BIN) serve --daemon

unserve: symfony ## Stop the web server
	$(SYMFONY_BIN) server:stop

# L70+12 => 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:populate

# L86+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 (replace index by the index name to delete)
	curl -X DELETE "localhost:9209/index?pretty"

## —— Docker 🐳 ————————————————————————————————————————————————————————————————
up: docker-compose.yaml ## Start the docker hub (MySQL,redis,adminer,elasticsearch,head,Kibana)
	$(DOCKER) -f docker-compose.yaml up -d

down: docker-compose.yaml ## Stop the docker hub
	$(DOCKER) down --remove-orphans

## —— Project 🐝 ———————————————————————————————————————————————————————————————
run: up wait reload serve ## Start docker, load fixtures, populate the Elasticsearch index and start the web server

reload: load-fixtures populate ## Reload fixtures and repopulate the Elasticserch index

abort: down unserve ## Stop docker and the Symfony binary server

cc-redis: ## Flush all Redis cache
	$(REDIS) 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) doctrine:fixtures:load -n
	$(SYMFONY) doctrine:schema:validate

init-snippet: ## Initialize a new snippet
	$(SYMFONY) $(PROJECT):init-snippet

## —— Tests ✅ —————————————————————————————————————————————————————————————————
test: phpunit.xml ## Launch main functionnal and unit tests
	./bin/phpunit --testsuite=main --stop-on-failure

test-external: phpunit.xml ## Launch tests implying external resources (api, services...)
	./bin/phpunit --testsuite=external --stop-on-failure

test-all: phpunit.xml ## Launch all tests
	./bin/phpunit --stop-on-failure --memory-limit

## —— Coding standards ✨ ——————————————————————————————————————————————————————
cs: codesniffer stan ## Launch check style and static analysis

codesniffer: ## Run php_codesniffer only
	./vendor/squizlabs/php_codesniffer/bin/phpcs --standard=phpcs.xml -n -p src/

stan: ## Run PHPStan only
	./vendor/bin/phpstan analyse -l max --memory-limit 1G -c phpstan.neon src/

psalm: ## Run psalm only
	./vendor/bin/psalm --show-info=false

init-psalm: ## Init a new psalm config file for a given level, it must be decremented to have stricter rules
	rm ./psalm.xml
	./vendor/bin/psalm --init src/ 3

cs-fix: ## Run php-cs-fixer and fix the code.
	./vendor/bin/php-cs-fixer fix src/

## —— Deploy 🚀 ————————————————————————————————————————————————————————————————
deploy: ## Full no-downtime deployment with EasyDeploy
	$(SYMFONY) deploy -v

env-check: ## Check the main ENV variables of the project
	printenv | grep -i app_

## —— Stats 📜 —————————————————————————————————————————————————————————————————
stats: ## Commits by 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); }'
En bonus, le snippet permettant d'utiliser ce code : 🎉
<?php declare(strict_types=1);

namespace App\Controller\Snippet;

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
 */
trait Snippet8Trait
{
    public function snippet8(): void
    {
        $process = new Process([
            'make',
            '-f',
            $this->kernel->getProjectDir().'/Makefile',
        ]);
        $process->run();

        $output = str_replace(
            ["\e[33m", "\e[32m", "\e[0m"],
            '',
            $process->getOutput()
        );

        echo $output; // That's it! 😁
    }
}

 Exécuter le snippet  ≪ showUnitTestButtonLabel ≫  Plus sur Stackoverflow   Lire la doc  Plus sur le web

<?php declare(strict_types=1);

namespace App\Tests\Controller\Snippets;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Process\Process;

/**
 * @covers Snippet
 */
final class Snippet8Test extends KernelTestCase
{
    protected function setUp(): void
    {
        self::bootKernel();
    }

    /**
     * @covers Snippet8Trait::snippet8
     *
     * @see https://www.strangebuzz.com/en/snippets/get-the-routing-information-of-the-referer
     */
    public function testSnippet8(): void
    {
        $process = new Process(['make', '-f', self::$kernel->getProjectDir().'/Makefile']);
        $process->run();
        $this->assertStringContainsStringIgnoringCase('The Strangebuzz Symfony Makefile', $process->getOutput());
    }
}