Skip to content

Instantly share code, notes, and snippets.

@zanbaldwin
Last active June 8, 2023 11:02
Show Gist options
  • Save zanbaldwin/5ea85ecf666174f23b1a0280540d5587 to your computer and use it in GitHub Desktop.
Save zanbaldwin/5ea85ecf666174f23b1a0280540d5587 to your computer and use it in GitHub Desktop.
Skeleton Makefile I reuse for Symfony projects.
zan@github.com ~/gists:
❯ make

Useful Shortcuts
 clear                         Clear multiple caches
 install                       (Re-)install all Composer dependencies
 migrate                       Run database migrations
 assets                        Build and Install Front-end Assets
 fix                           Run PHP-CS-Fixer
 stan                          Run static analysis via PHPStan
 unit                          Run PHPUnit in parallel via Paratest

Cache
 clear-cache                   Clear the Symfony cache
 clear-stan                    Clear the PHPStan result cache
Workers
 queue                         Run the queue worker in the foreground

Development
 ssl                           Generate SSL certificates for local HTTPS development
 password                      Generates a random password for the dev database
Database Snapshotting
 dump                          Dump a copy of the dev database.
 restore                       Restore a dev database dump (make restore DUMP="filename.sql")
SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
.ONESHELL:
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
ifeq ($(origin .RECIPEPREFIX), undefined)
$(error This Make does not support .RECIPEPREFIX; Please use GNU Make 4.0 or later)
endif
# The editor config for IDEs automatically converts tabs (default Make config) to spaces. Use a printable character instead of whitespace.
.RECIPEPREFIX = >
THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)
THIS_MAKEFILE:=$(notdir $(THIS_MAKEFILE_PATH))
# As the first listed recipe, usage will print the available Make recipes available in this file.
usage:
> @grep -E '(^[a-zA-Z_-]+:\s*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.?## "}; {printf "\033[32m %-30s\033[0m%s\n", $$1, $$2}' | sed -e 's/\[32m ## /[33m/'
.PHONY: usage
.SILENT: usage
vars:
> @$(foreach V,$(sort $(.VARIABLES)), $(if $(filter-out environment% default automatic, $(origin $V)),$(warning $V = $(value $V))))
.PHONY: vars
.SILENT: vars
# Useful as a dependency of other recipes that require root (eg, start Nginx on privileged ports 80/443).
require-root:
> [ "$$(id -u)" == "0" ] || { echo "This command must be run as root. Please retry with sudo."; exit 1; }
.PHONY: require-root
.SILENT: require-root
# Useful as a dependency of other recipes that require Docker.
require-docker:
> command -v "docker" >/dev/null 2>&1 || { echo >&2 "Docker client required for command not found (PATH: \"$${PATH}\")."; exit 1; }
> docker info >/dev/null 2>&1 || { echo >&2 "Docker daemon unavailable. Perhaps retry as root/sudo?"; exit; }
> command -v "docker-compose" >/dev/null 2>&1 || { echo >&2 "Docker Compose required for command not found (PATH: \"$${PATH}\")."; exit 1; }
.PHONY: require-docker
.SILENT: require-docker
## Useful Shortcuts
clear: ## Clear multiple caches
clear: clear-cache clear-stan
.PHONY: clear
.SILENT: clear
# Do not use this recipe as a dependency of other recipes because it will get
# run every time, not just when the file doesn't exist.
install: ## (Re-)install all Composer dependencies
install:
> XDEBUG_MODE=off composer install --working-dir "$(THIS_DIR)"
.PHONY: install
.SILENT: install
migrate: ## Run database migrations
migrate:
> XDEBUG_MODE=off "$(THIS_DIR)/bin/console" doctrine:migrations:migrate --no-interaction
.PHONY: migrate
.SILENT: migrate
assets: ## Build and Install Front-end Assets
assets:
> XDEBUG_MODE=off "$(THIS_DIR)/bin/console" assets:install
> command -v "yarn" >/dev/null 2>&1 && { \
echo 'Building via Yarn Locally'; \
(cd "$(THIS_DIR)"; yarn install && yarn build); \
exit 0; \
}
> command -v "docker" >/dev/null 2>&1 && { \
echo 'Building via Yarn in Docker'; \
docker info >/dev/null 2>&1 || { echo >&2 "Docker is installed but the daemon is unavailable. Check Docker permissions or install Yarn locally."; exit 1; }; \
docker run --rm -it --volume "$(THIS_DIR):/srv" --workdir "/srv" --user "$$(id -u):$$(id -g)" -e "HOME=/tmp" "node:18-alpine" sh -c "yarn install && yarn build;"; \
exit 0; \
}
> echo 'Neither Yarn nor Docker available to build front-end assets.' >&2
> exit 1
.PHONY: assets
.SILENT: assets
$(THIS_DIR)/vendor/autoload.php:
> XDEBUG_MODE=off composer install --working-dir "$(THIS_DIR)"
$(THIS_DIR)/vendor/bin/php-cs-fixer:
> XDEBUG_MODE=off composer install --working-dir "$(THIS_DIR)"
$(THIS_DIR)/vendor/bin/phpstan:
> XDEBUG_MODE=off composer install --working-dir "$(THIS_DIR)"
$(THIS_DIR)/vendor/bin/paratest:
> XDEBUG_MODE=off composer install --working-dir "$(THIS_DIR)"
fix: ## Run PHP-CS-Fixer
fix: $(THIS_DIR)/vendor/bin/php-cs-fixer
> (cd "$(THIS_DIR)"; XDEBUG_MODE=off PHP_CS_FIXER_IGNORE_ENV=1 "$(THIS_DIR)/vendor/bin/php-cs-fixer" fix)
.PHONY: fix
.SILENT: fix
stan: ## Run static analysis via PHPStan
stan: $(THIS_DIR)/vendor/bin/phpstan
> (cd "$(THIS_DIR)"; XDEBUG_MODE=off "$(THIS_DIR)/vendor/bin/phpstan" -v)
.PHONY: stan
.SILENT: stan
unit: ## Run PHPUnit in parallel via Paratest
unit: $(THIS_DIR)/vendor/bin/paratest
> (cd "$(THIS_DIR)"; "$(THIS_DIR)/vendor/bin/paratest" "--processes=6" "--runner=WrapperRunner" --configuration="tests/phpunit.xml)
.PHONY: unit
.SILENT: unit
##
## Cache
clear-cache: ## Clear the Symfony cache
clear-cache:
> rm -rf "$(THIS_DIR)/var/cache"
> XDEBUG_MODE=off php "$(THIS_DIR)/vendor/bin/console" "cache:clear"
.PHONY: clear-cache
.SILENT: clear-cache
clear-stan: ## Clear the PHPStan result cache
clear-stan:
> (cd "$(THIS_DIR)"; XDEBUG_MODE=off php "$(THIS_DIR)/vendor/bin/phpstan" "clear-result-cache")
.PHONY: clear-stan
.SILENT: clear-stan
## Workers
queue: ## Run the queue worker in the foreground
queue: $(THIS_DIR)/vendor/autoload.php
> php "$(THIS_DIR)/bin/console" "messenger:consume" -vv async
.PHONY: queue
.SILENT: queue
##
## Development
ssl: ## Generate SSL certificates for local HTTPS development
ssl:
> command -v "mkcert" >/dev/null 2>&1 || { echo >&2 "Please install MkCert for Development."; exit 1; }
> mkdir -p "$(THIS_DIR)/build/ssl"
> mkcert \
-cert-file "$(THIS_DIR)/build/ssl/fullchain.pem" \
-key-file "$(THIS_DIR)/build/ssl/privkey.pem" \
"your-project.test" \
"localhost" \
"127.0.0.1"
> cp "$$(mkcert -CAROOT)/rootCA.pem" "$(THIS_DIR)/build/ssl/ca.pem"
> cp "$(THIS_DIR)/docker/dev/.ssl/fullchain.pem" "$(THIS_DIR)/build/ssl/chain.pem"
> openssl dhparam -out "$(THIS_DIR)/build/ssl/dhparam.pem" 1024
.PHONY: ssl
.SILENT: ssl
password: ## Generates a random password for the dev database
password:
> mkdir -p "$(THIS_DIR)/build/secrets"
> [ ! -f "$(THIS_DIR)/build/secrets/dbpass" ] || { \
echo >&2 "$$(tput setaf 1)A password has already been created. Remove the file \"$(THIS_DIR)/build/secrets/dbpass\" to try again.$$(tput sgr0)"; \
echo >&2 "$$(tput setaf 1)Double check that you're NOT REMOVING THE ONLY COPY OF YOUR EXISTING PASSWORD.$$(tput sgr0)"; \
exit 1; \
}
> touch "$(THIS_DIR)/build/secrets/dbpass"
> echo "$$(date "+%s.%N" | sha256sum | base64 | head -c 40)" > "$(THIS_DIR)/build/secrets/dbpass"
> echo >&2 "$$(tput setaf 2)Database password generated and placed in file \"$(THIS_DIR)/build/secrets/dbpass\".$$(tput sgr0)"
.PHONY: password
.SILENT: password
## Database Snapshotting
dump: ## Dump a copy of the dev database.
dump:
> command -v "pg_dump" >/dev/null 2>&1 || { echo >&2 "Postgres Dump Tool not available on \$$PATH."; exit 1; }
> mkdir -p "$(THIS_DIR)/var/dumps"
> export DUMP_FILENAME="$$(date -u '+%Y%m%dT%H%m%SZ').sql"
> export DUMP_FILEPATH="$(THIS_DIR)/var/dumps/$${DUMP_FILENAME}"
> PGPASSWORD="$$(cat "$(THIS_DIR)/build/secrets/dbpass")" pg_dump --host="127.0.0.1" --port="5432" --username="root" --dbname="your_project" --format=p --blobs --clean --create --no-owner --column-inserts --disable-dollar-quoting --if-exists --inserts --quote-all-identifiers >"$${DUMP_FILEPATH}"
> echo "New database dump saved at \"$${DUMP_FILEPATH}\". Use the following command to restore the manager database back to this point:"
> echo "make restore DUMP=\"$${DUMP_FILENAME}\""
.PHONY: dump
.SILENT: dump
DUMP := ""
restore: ## Restore a dev database dump (make restore DUMP="filename.sql")
restore:
> command -v "psql" >/dev/null 2>&1 || { echo >&2 "Postgres CLI Tool not available on \$$PATH."; exit 1; }
> [ ! -z "$(DUMP)" ] || { echo "DUMP argument not supplied, usage: make restore DUMP=\"filename.sql\""; exit 2; }
> [ -f "$(THIS_DIR)/var/dumps/$(DUMP)" ] || { echo "Cannot restore database dump. File \"$(THIS_DIR)/var/dumps/$(DUMP)\" does not exist."; exit 2; }
> PGPASSWORD="$$(cat "$(THIS_DIR)/build/secrets/dbpass")" psql --host="127.0.0.1" --port="5432" --username="root" --dbname="your_project" < "$(THIS_DIR)/var/dumps/$(DUMP)"
> echo "Database dump \"$(THIS_DIR)/var/dumps/$(DUMP)\" restored."
.PHONY: restore
.SILENT: restore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment