The perfect MakeFile for Symfony

Published on 2018-12-09 • Modified on 2018-12-09

I have always used shell (.sh) scripts to streamline all my dev tasks on Symfony projects. But I recently discovered (here and here) that we could use a Makefile for this. I must say It really works well because:
1) It allows to document all your dev tasks in a single file.
2) You've got autocomletion of the tasks name as Make a standard Unix tool.
3) The task will stop as soon as an error is returned by one of the included command.
The file below is the one actually used by the Strangebuzz.com project and you can test it live to see what is the default output. Which is in fact the "help" command that lists all that can be done. Don't forget to change the PROJECT parameter at the top of the file. 😉
BONUS: The PHP code to get the output of the system call thanks to the Symfony Process component.


# —— Inspired by ———————————————————————————————————————————————————————————————
# 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 —————————————————————————————————————————————————————————————————————————
PROJECT    = strangebuzz
EXEC_PHP   = php
REDIS      = redis-cli
GIT        = git
GIT_AUTHOR = COil
SYMFONY    = $(EXEC_PHP) bin/console
COMPOSER   = $(EXEC_PHP) composer.phar
.DEFAULT_GOAL := help

## —— 🐝  Strangebuzz Make file  🐝  —————————————————————————————————————————————
help: ## Outputs this help screen
	@grep -E '(^[a-zA-Z_-]+:.*?##.*$$)|(^##)' $(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

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

check: ## Check dependencies
	php vendor/bin/composer-require-checker check composer.json

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

cc: ## Clear cache
	$(SYMFONY) c:c

warmup: ## Warmump the cache
	$(SYMFONY) cache:warmup

fix-perms: ## Fix permissions of all var files
	chmod -R 777 var/*

start: ## Start the local Symfony web server
	$(SYMFONY) server:start

stop: ## Stop the local Symfony web server
	$(SYMFONY) server:stop

assets: ## Install the assets with symlinks in the public folder (web)
	$(SYMFONY) assets:install web/ --symlink  --relative

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

## —— Project ——————————————————————————————————————————————————————————————————
cc-redis: ## Flush all Redis cache
	$(REDIS) flushall

commands: ## Display all Strangebuzz specfic commands
	$(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:migration:status

cs: ## Launch check style and static analysis
	./vendor/squizlabs/php_codesniffer/bin/phpcs --standard=phpcs.xml -n -p src/
	./vendor/bin/phpstan analyse -l 7 -c phpstan.neon src/

test: phpunit.xml.dist load-fixtures ## Launch all functionnal and unit tests
	bin/phpunit --stop-on-failure

## —— Deploy ——————————————————————————————————————————————————————————————————
deploy: ## Deploy, install composer dependencies and run database migrations
	bin/prod/deploy.sh

git-update: ## Update Git only and refresh cache (sf+pagespeed)
	git checkout public/index_dev.php
	git pull
	rm -rf var/cache/* var/logs/*
	php bin/console cache:warmup
	chmod -R 777 var/*
	touch /var/cache/mod_pagespeed/cache.flush
	rm public/index_dev.php

## —— 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); }'
Bonus, the snippet to run this code: 🎉
<?php declare(strict_types=1);

namespace App\Controller\Snippet;

use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Process\Process;

/**
 * I am using a PHP trait in order to isolate each snippet in a file.
 * This code should be called from a Symfony controller extending AbstractController (as of Sf 4.2)
 * or Symfony\Bundle\FrameworkBundle\Controller\Controller (Sf <= 4.1)
 * Services are injected in the controller constructor.
 *
 * @see https://symfony.com/doc/current/components/process.html
 */
trait Snippet8Trait
{
    public function snippet8(): void
    {
        if (!$this->kernel instanceof KernelInterface) {
            throw new \RuntimeException("Houston, We've Got a Problem. 💥");
        }

        $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! 😁
    }
}

 Run this snippet  More on Stackoverflow