SlideShare a Scribd company logo
1 of 119
Download to read offline
Makefiles in 2020
Why they still matter
👋
Simon
Release Engineer @trivago_tech
✋
✋
multiple projects
✋
many similar projects
✋
many different projects
✋
projects with multiple languages
✋
Makefiles
If you have worked in a company with many different
projects for long enough …
Congratulations 🎂
You now work on a new*
project!
Congratulations 🎂
You now work on a new*
project!
*
new for you
team restruture
co-worker left the company
abandoned project as a dependency
you adopt a shiny, new puppy 🐶
it’s 1 AM 🕐
the website is broken 🧨
you need to release a fix 🚒
So how do you do that?
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you build that?
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you build that?
python3 ./setup.py bdist_wheel
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you test that?
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you test that?
python3 ./setup.py test
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you test that?
python3 ./setup.py test
python3 -m pytest
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you test that?
python3 ./setup.py test
python3 -m pytest
tox
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you install that?
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
pip3 install --upgrade ./
cp -r ./{event,inventory}_rules 
/etc/resourcemanagement
install ./inventory_crawler.timer 
/etc/systemd/system/inventory_crawler.timer
install ./inventory_crawler.service 
/etc/systemd/system/inventory_crawler.service
install ./cloudtrail_crawler.service 
/etc/systemd/system/cloudtrail_crawler.service
useradd -r resourcemanagement
systemctl daemon-reload
How do you install that?
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you deploy that?
$ ls cf-templates/
EC2Machine.yaml
MonitoringRoleCreation.yaml
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you deploy that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
How do you test that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
How do you test that?
composer install
composer test
npm ci
npm test
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
How do you build that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
DIST_DIR=$(pwd)/dist
mkdir -p "${DIST_DIR}"
composer install --no-interaction --no-dev --optimize-autoloader --profile
npm ci
npm run build
# custom commands
S_CONSOLE="php -d memory_limit=2048M ./app/${APP_NAME}/console"
${S_CONSOLE} cache:clear --no-warmup --env=prod --no-debug
${S_CONSOLE} cache:warmup --env=prod --no-debug
${S_CONSOLE} assets:install --env=prod --no-debug ./web/"${RELEASE_VERSION}$
{RELEASE_SUBVERSION}"
${S_CONSOLE} company:asset-dump --env=prod --no-debug
${S_CONSOLE} company:container-dump --env=prod --no-debug
# remove stuff we don't want in our packages'
rm web/*_dev.php web/*_integration.php web/*_jenkins.php
rm -rf node_modules test-js test test-trupi
# package PHP
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf 
"${DIST_DIR}/${PACKAGE_ID}.tar.gz" *
# package PHP cache
pushd /var/www/prod
find ${PACKAGE_ID} -type d -exec chmod 777 {} +
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf 
"${DIST_DIR}/${PACKAGE_ID}.cache.tar.gz" ${PACKAGE_ID}
popd
# package assets
cd ./web
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf 
“${DIST_DIR}/${PACKAGE_ID}.assets.tar.gz" v*
How do you build that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
DIST_DIR=$(pwd)/dist
mkdir -p "${DIST_DIR}"
composer install --no-interaction --no-dev --optimize-autoloader --profile
npm ci
npm run build
# custom commands
S_CONSOLE="php -d memory_limit=2048M ./app/${APP_NAME}/console"
${S_CONSOLE} cache:clear --no-warmup --env=prod --no-debug
${S_CONSOLE} cache:warmup --env=prod --no-debug
${S_CONSOLE} assets:install --env=prod --no-debug ./web/"${RELEASE_VERSION}$
{RELEASE_SUBVERSION}"
${S_CONSOLE} company:asset-dump --env=prod --no-debug
${S_CONSOLE} company:container-dump --env=prod --no-debug
# remove stuff we don't want in our packages'
rm web/*_dev.php web/*_integration.php web/*_jenkins.php
rm -rf node_modules test-js test test-trupi
# package PHP
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf 
"${DIST_DIR}/${PACKAGE_ID}.tar.gz" *
# package PHP cache
pushd /var/www/prod
find ${PACKAGE_ID} -type d -exec chmod 777 {} +
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf 
"${DIST_DIR}/${PACKAGE_ID}.cache.tar.gz" ${PACKAGE_ID}
popd
# package assets
cd ./web
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf 
“${DIST_DIR}/${PACKAGE_ID}.assets.tar.gz" v*
How do you build that?
Obviously 🙄
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
How do you deploy that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
How do you deploy that?
Good luck with that 🍀
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
So how do we solve that?
README
README
Everybody can read it, many won’t
README
quickly outdated
README
copy pasta 🍝
CI/CD pipeline
CI/CD pipeline
continuous delivery for free*
CI/CD pipeline
continuous delivery for free*
*
not really
CI/CD pipeline
everybody can use it*
CI/CD pipeline
everybody can use it*
*
if they know the URLs
CI/CD pipeline
debugging/fixing pipelines 🔥
language specific tools
language specific tools
great for pros
language specific tools
OK for beginners
language specific tools
and then you add a 2nd language
Solution?
Add a README
Wrong!
Use a language agnostic build tool
But which one?
CMake, redo, Gradle, Meson, SCons, Ninja, ant, Bazel, tup, …
CMake, redo, Gradle, Meson, SCons, Ninja, ant, Bazel, tup, …
✋
Let’s start with a simple build.sh
#!/bin/bash
composer install 
--no-interaction 
--no-dev
npm ci
npm run build
tar czf dist/$1.tar.gz web/
#!/bin/bash
composer install 
--no-interaction 
--no-dev
npm ci
npm run build
tar czf dist/$1.tar.gz web/
no -x
#!/bin/bash
composer install 
--no-interaction 
--no-dev
npm ci
npm run build
tar czf dist/$1.tar.gz web/
no -e
#!/bin/bash
composer install 
--no-interaction 
--no-dev
npm ci
npm run build
tar czf dist/$1.tar.gz web/
no tests
#!/bin/bash
composer install 
--no-interaction 
--no-dev
npm ci
npm run build
tar czf dist/$1.tar.gz web/
needs an argument
#!/bin/bash
composer install 
--no-interaction 
--no-dev
npm ci
npm run build
tar czf dist/$1.tar.gz web/
needs an argument
weird issues
Let’s fix that
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install 
--no-interaction 
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz 
web/ 
vendor/
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install 
--no-interaction 
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz 
web/ 
vendor/
a bit verbose
not simple to just run the tests
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install 
--no-interaction 
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz 
web/ 
vendor/
re-installs all npm packages
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install 
--no-interaction 
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz 
web/ 
vendor/
more and more cases
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install 
--no-interaction 
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz 
web/ 
vendor/
more and more scripts
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install 
--no-interaction 
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz 
web/ 
vendor/
good luck 🍀
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install 
--no-interaction 
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz 
web/ 
vendor/
Makefiles to the rescue!
But why Makefiles?
But why Makefiles?
execute steps in the shell
But why Makefiles?
we already have most of that
But why Makefiles?
-ex for free*
But why Makefiles?
-ex for free*
*
for real this time
But why Makefiles?
installed virtually everywhere
But why Makefiles?
dependencies for steps
But why Makefiles?
file based dependencies
But why Makefiles?
handbook of common tasks
But why Makefiles?
very useful at 1 AM 🕐
But why Makefiles?
or with a new puppy 🐶
How would that look like?
test: dependencies
composer test
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies:
composer install --no-interaction --no-dev
npm install
.PHONY: test build dependencies
test: dependencies
composer test
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies:
composer install --no-interaction --no-dev
npm install
.PHONY: test build dependencies
test: dependencies
composer test
npm test
test: dependencies
composer test
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies:
composer install --no-interaction --no-dev
npm install
.PHONY: test build dependencies
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
test: dependencies
composer test
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies:
composer install --no-interaction --no-dev
npm install
.PHONY: test build dependencies
dependencies:
composer install --no-interaction --no-dev
npm install
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
$ make build PACKAGE_ID=my_package-1.2.9
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
$ make build
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
# guard against being called without parameters
# or environment variables
env-%:
test -n "${$*}"  
(echo "Argument $* not set"; exit 1)
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js env-%
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
[…]
# guard against being called without parameters
# or environment variables
env-%:
test -n "${$*}"  
(echo "Argument $* not set"; exit 1)
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
# guard against being called without parameters
# or environment variables
env-%:
@test -n "${$*}"  
(echo "Argument $* not set"; exit 1)
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js env-%
# guard against being called without parameters
# or environment variables
env-%:
@test -n "${$*}"  
(echo "Argument $* not set"; exit 1)
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
# guard against being called without parameters
# or environment variables
env-%:
@test -n "${$*}"  
(echo "Argument $* not set"; exit 1)
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js env-%
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := "my_package-${VERSION}"
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := "my_package-${VERSION}"
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
DOCKER_REGISTRY := "docker.company.com/"
DOCKER_NAME := "my-project:${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
docker: dist/${PACKAGE_ID}.tar.gz
docker build 
--build-arg PACKAGE=$< 
-t "${DOCKER_REGISTRY}${DOCKER_NAME}" 
./
push: docker
docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js docker push
DOCKER_REGISTRY := "docker.company.com/"
DOCKER_NAME := “my-project:${VERSION}"
docker: dist/${PACKAGE_ID}.tar.gz
docker build 
--build-arg PACKAGE=$< 
-t "${DOCKER_REGISTRY}${DOCKER_NAME}" 
./
push: docker
docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
DOCKER_REGISTRY := "docker.company.com/"
DOCKER_NAME := "my-project:${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
docker: dist/${PACKAGE_ID}.tar.gz
docker build 
--build-arg PACKAGE=$< 
-t "${DOCKER_REGISTRY}${DOCKER_NAME}" 
./
push: docker
docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
deploy: push
kubectl apply -f k8s/deployment.yaml
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies dependencies-php
dependencies-js docker push
deploy: push
kubectl apply -f k8s/deployment.yaml
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
DOCKER_REGISTRY := "docker.company.com/"
DOCKER_NAME := "my-project:${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
docker: dist/${PACKAGE_ID}.tar.gz
docker build 
--build-arg PACKAGE=$< 
-t "${DOCKER_REGISTRY}${DOCKER_NAME}" 
./
push: docker
docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
deploy: k8s/deployment.yaml push
kubectl apply -f $<
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
k8s/deployment.yaml: k8s/deployment.yaml.template
sed 's#my-project:VERSION#${DOCKER_NAME}#' 
> $@
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies dependencies-php
dependencies-js docker push k8s/deployment.yaml
deploy: k8s/deployment.yaml push
kubectl apply -f $<
k8s/deployment.yaml: k8s/deployment.yaml.template
sed 's#my-project:VERSION#${DOCKER_NAME}#' 
> $@
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
DOCKER_REGISTRY := "docker.company.com/"
DOCKER_NAME := "my-project:${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
docker: dist/${PACKAGE_ID}.tar.gz
docker build 
--build-arg PACKAGE=$< 
-t "${DOCKER_REGISTRY}${DOCKER_NAME}" 
./
push: docker
docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
deploy: k8s/deployment.yaml push
kubectl apply -f $<
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
k8s/deployment.yaml: k8s/deployment.yaml.template
sed 's#my-project:VERSION#${DOCKER_NAME}#' 
> $@
clean:
rm -rf ./vendor/ # php packages
npm clean
rm k8s/deployment.yaml
distclean:
rm dist/*.tar.gz
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml clean distclean
clean:
rm -rf ./vendor/ # php packages
npm clean
rm k8s/deployment.yaml
distclean:
rm dist/*.tar.gz
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
DOCKER_REGISTRY := "docker.company.com/"
DOCKER_NAME := "my-project:${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
docker: dist/${PACKAGE_ID}.tar.gz
docker build 
--build-arg PACKAGE=$< 
-t "${DOCKER_REGISTRY}${DOCKER_NAME}" 
./
push: docker
docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
deploy: k8s/deployment.yaml push
kubectl apply -f $<
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
k8s/deployment.yaml: k8s/deployment.yaml.template
sed 's#my-project:VERSION#${DOCKER_NAME}#' 
> $@
clean:
rm -rf ./vendor/ # php packages
npm clean
rm k8s/deployment.yaml
distclean:
rm dist/*.tar.gz
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml clean distclean
In your CI/CD system:
$ make test deploy
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
DOCKER_REGISTRY := "docker.company.com/"
DOCKER_NAME := "my-project:${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
docker: dist/${PACKAGE_ID}.tar.gz
docker build 
--build-arg PACKAGE=$< 
-t "${DOCKER_REGISTRY}${DOCKER_NAME}" 
./
push: docker
docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
deploy: k8s/deployment.yaml push
kubectl apply -f $<
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
k8s/deployment.yaml: k8s/deployment.yaml.template
sed 's#my-project:VERSION#${DOCKER_NAME}#' 
> $@
clean:
rm -rf ./vendor/ # php packages
npm clean
rm k8s/deployment.yaml
distclean:
rm dist/*.tar.gz
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml clean distclean
On your development system:
$ make test
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
DOCKER_REGISTRY := "docker.company.com/"
DOCKER_NAME := "my-project:${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
docker: dist/${PACKAGE_ID}.tar.gz
docker build 
--build-arg PACKAGE=$< 
-t "${DOCKER_REGISTRY}${DOCKER_NAME}" 
./
push: docker
docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
deploy: k8s/deployment.yaml push
kubectl apply -f $<
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
k8s/deployment.yaml: k8s/deployment.yaml.template
sed 's#my-project:VERSION#${DOCKER_NAME}#' 
> $@
clean:
rm -rf ./vendor/ # php packages
npm clean
rm k8s/deployment.yaml
distclean:
rm dist/*.tar.gz
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml clean distclean
At 1 AM 🕐 to release a fix 🚒:
$ make deploy
make
DevOpsDev Ops
end;
@m3t0r
https://www.gnu.org/software/make/manual/make.html
Resources:
common Makefile targets
libraries applications docker-based projects
all ✅ ✅ ✅
build ✅ ✅ ✅
debug ✅ ✅ ✅
test ✅ ✅ ✅
release ✅ ✅
push ✅
deploy ✅ ✅
clean ✅ ✅ ✅
distclean ✅ ✅
install ✅
up ✅
see make manual — 16.6 Standard Targets for Users
- FOO = "value"

The value is recursively computed every time FOO is being
accessed.
- FOO := "value"

The value is directly computed and assigned once to FOO.
- FOO ?= "value"

The value is lazily set if FOO has no value at the moment it
is being accessed.
- FOO += "value"

The value is appended to FOO with a space in front.
see make manual — 6.2 The Two Flavors of Variables
The different kinds of assignments
As an argument (better):
$ make target FOO=bar
As an environment variable (requires ?= in Makefile):
$ FOO=bar make target
Setting variables on make invocation
see make manual — 9.5 Overriding Variables
Dos
- Fan out from general targets to more specific ones: deploy " deploy-nomad, not deploy-
nomad " deploy TOOL=nomad, use pattern rules to accomplish this with similar targets.
- Split up big targets into smaller chunks; that gives the user the possibility to not
have to run everything but only the parts the they are interested in: test " test-unit,
test-integration, test-codestyle, then the user can just call make test-unit.
- Name targets after files where-ever possible. It will reduce the amount of unnecessary
computation for invocations.
- Use variables to abstract common command calls. ${MAKE} and ${CC} are standards. Why not
add ${GREP} or ${DOCKER}, so users can point your Makefile to the binary they want to
use for those programs?
Don’ts
- Use :: to define phony rules. That’s just a side-effect of their main purpose (to split
one target over multiple Makefiles).
- Add to many targets that aren’t used on a regular basis. They will become stale without
you noticing.

More Related Content

Similar to Makefiles in 2020 — Why they still matter

Continuous Delivery com Docker, OpenShift e Jenkins
Continuous Delivery com Docker, OpenShift e JenkinsContinuous Delivery com Docker, OpenShift e Jenkins
Continuous Delivery com Docker, OpenShift e JenkinsBruno Padilha
 
Mobile development in 2020
Mobile development in 2020 Mobile development in 2020
Mobile development in 2020 Bogusz Jelinski
 
ngManila - Codename: Fireball - Hello World in Angular
ngManila - Codename: Fireball - Hello World in AngularngManila - Codename: Fireball - Hello World in Angular
ngManila - Codename: Fireball - Hello World in Angularngmanila
 
Automatisation in development and testing - within budget
Automatisation in development and testing - within budgetAutomatisation in development and testing - within budget
Automatisation in development and testing - within budgetDavid Lukac
 
Django dev-env-my-way
Django dev-env-my-wayDjango dev-env-my-way
Django dev-env-my-wayRobert Lujo
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014biicode
 
Hosting Your Own OTA Update Service
Hosting Your Own OTA Update ServiceHosting Your Own OTA Update Service
Hosting Your Own OTA Update ServiceQuinlan Jung
 
Practical guide for front-end development for django devs
Practical guide for front-end development for django devsPractical guide for front-end development for django devs
Practical guide for front-end development for django devsDavidson Fellipe
 
PHP Mega Meetup, Sep, 2020, Anti patterns in php
PHP Mega Meetup, Sep, 2020, Anti patterns in phpPHP Mega Meetup, Sep, 2020, Anti patterns in php
PHP Mega Meetup, Sep, 2020, Anti patterns in phpAhmed Abdou
 
2018 the conf put git to work - increase the quality of your rails project...
2018 the conf   put git to work -  increase the quality of your rails project...2018 the conf   put git to work -  increase the quality of your rails project...
2018 the conf put git to work - increase the quality of your rails project...Rodrigo Urubatan
 
May The Nodejs Be With You
May The Nodejs Be With YouMay The Nodejs Be With You
May The Nodejs Be With YouDalibor Gogic
 
Rapid Prototyping FTW!!!
Rapid Prototyping FTW!!!Rapid Prototyping FTW!!!
Rapid Prototyping FTW!!!cloudbring
 
Modern Release Engineering in a Nutshell - Why Researchers should Care!
Modern Release Engineering in a Nutshell - Why Researchers should Care!Modern Release Engineering in a Nutshell - Why Researchers should Care!
Modern Release Engineering in a Nutshell - Why Researchers should Care!Bram Adams
 
Deploying Symfony | symfony.cat
Deploying Symfony | symfony.catDeploying Symfony | symfony.cat
Deploying Symfony | symfony.catPablo Godel
 
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...Sébastien Morel
 
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...eZ Systems
 
An OpenShift Primer for Developers to get your Code into the Cloud (PTJUG)
An OpenShift Primer for Developers to get your Code into the Cloud (PTJUG)An OpenShift Primer for Developers to get your Code into the Cloud (PTJUG)
An OpenShift Primer for Developers to get your Code into the Cloud (PTJUG)Eric D. Schabell
 
CoffeeScript: A beginner's presentation for beginners copy
CoffeeScript: A beginner's presentation for beginners copyCoffeeScript: A beginner's presentation for beginners copy
CoffeeScript: A beginner's presentation for beginners copyPatrick Devins
 

Similar to Makefiles in 2020 — Why they still matter (20)

Continuous Delivery com Docker, OpenShift e Jenkins
Continuous Delivery com Docker, OpenShift e JenkinsContinuous Delivery com Docker, OpenShift e Jenkins
Continuous Delivery com Docker, OpenShift e Jenkins
 
Mobile development in 2020
Mobile development in 2020 Mobile development in 2020
Mobile development in 2020
 
ngManila - Codename: Fireball - Hello World in Angular
ngManila - Codename: Fireball - Hello World in AngularngManila - Codename: Fireball - Hello World in Angular
ngManila - Codename: Fireball - Hello World in Angular
 
Automatisation in development and testing - within budget
Automatisation in development and testing - within budgetAutomatisation in development and testing - within budget
Automatisation in development and testing - within budget
 
Django dev-env-my-way
Django dev-env-my-wayDjango dev-env-my-way
Django dev-env-my-way
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
 
Hosting Your Own OTA Update Service
Hosting Your Own OTA Update ServiceHosting Your Own OTA Update Service
Hosting Your Own OTA Update Service
 
Practical guide for front-end development for django devs
Practical guide for front-end development for django devsPractical guide for front-end development for django devs
Practical guide for front-end development for django devs
 
PHP Mega Meetup, Sep, 2020, Anti patterns in php
PHP Mega Meetup, Sep, 2020, Anti patterns in phpPHP Mega Meetup, Sep, 2020, Anti patterns in php
PHP Mega Meetup, Sep, 2020, Anti patterns in php
 
Buri2019
Buri2019Buri2019
Buri2019
 
2018 the conf put git to work - increase the quality of your rails project...
2018 the conf   put git to work -  increase the quality of your rails project...2018 the conf   put git to work -  increase the quality of your rails project...
2018 the conf put git to work - increase the quality of your rails project...
 
May The Nodejs Be With You
May The Nodejs Be With YouMay The Nodejs Be With You
May The Nodejs Be With You
 
Rapid Prototyping FTW!!!
Rapid Prototyping FTW!!!Rapid Prototyping FTW!!!
Rapid Prototyping FTW!!!
 
Modern Release Engineering in a Nutshell - Why Researchers should Care!
Modern Release Engineering in a Nutshell - Why Researchers should Care!Modern Release Engineering in a Nutshell - Why Researchers should Care!
Modern Release Engineering in a Nutshell - Why Researchers should Care!
 
Deploying Symfony | symfony.cat
Deploying Symfony | symfony.catDeploying Symfony | symfony.cat
Deploying Symfony | symfony.cat
 
Demystifying Maven
Demystifying MavenDemystifying Maven
Demystifying Maven
 
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
 
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
eZ Platform Cloud and eZ Launchpad: Don’t Host, Don’t Deploy, Don’t Install—J...
 
An OpenShift Primer for Developers to get your Code into the Cloud (PTJUG)
An OpenShift Primer for Developers to get your Code into the Cloud (PTJUG)An OpenShift Primer for Developers to get your Code into the Cloud (PTJUG)
An OpenShift Primer for Developers to get your Code into the Cloud (PTJUG)
 
CoffeeScript: A beginner's presentation for beginners copy
CoffeeScript: A beginner's presentation for beginners copyCoffeeScript: A beginner's presentation for beginners copy
CoffeeScript: A beginner's presentation for beginners copy
 

Recently uploaded

Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 

Recently uploaded (20)

Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 

Makefiles in 2020 — Why they still matter

  • 1. Makefiles in 2020 Why they still matter
  • 5.
  • 11. If you have worked in a company with many different projects for long enough …
  • 12. Congratulations 🎂 You now work on a new* project!
  • 13. Congratulations 🎂 You now work on a new* project! * new for you
  • 16. abandoned project as a dependency
  • 17. you adopt a shiny, new puppy 🐶
  • 18. it’s 1 AM 🕐 the website is broken 🧨 you need to release a fix 🚒
  • 19. So how do you do that?
  • 28. $ ls README.md cf-templates/ cloudtrail_crawler.service event_rules/ inventory_crawler.service inventory_crawler.timer inventory_rules/ resourcemanagement/ setup.py tests/ tox.ini pip3 install --upgrade ./ cp -r ./{event,inventory}_rules /etc/resourcemanagement install ./inventory_crawler.timer /etc/systemd/system/inventory_crawler.timer install ./inventory_crawler.service /etc/systemd/system/inventory_crawler.service install ./cloudtrail_crawler.service /etc/systemd/system/cloudtrail_crawler.service useradd -r resourcemanagement systemctl daemon-reload How do you install that?
  • 30. $ ls cf-templates/ EC2Machine.yaml MonitoringRoleCreation.yaml $ ls README.md cf-templates/ cloudtrail_crawler.service event_rules/ inventory_crawler.service inventory_crawler.timer inventory_rules/ resourcemanagement/ setup.py tests/ tox.ini How do you deploy that?
  • 32. How do you test that? $ ls CHANGELOG.md Dockerfile Patches/ README.md app/ bin/ composer.json composer.lock package-lock.json package.json php72-compatibility-fix.patch postcss.config.js sonar-project.properties src/ src-js/ test/ test-trupi/ web/ webpack.config.js
  • 33. How do you test that? composer install composer test npm ci npm test $ ls CHANGELOG.md Dockerfile Patches/ README.md app/ bin/ composer.json composer.lock package-lock.json package.json php72-compatibility-fix.patch postcss.config.js sonar-project.properties src/ src-js/ test/ test-trupi/ web/ webpack.config.js
  • 34. How do you build that? $ ls CHANGELOG.md Dockerfile Patches/ README.md app/ bin/ composer.json composer.lock package-lock.json package.json php72-compatibility-fix.patch postcss.config.js sonar-project.properties src/ src-js/ test/ test-trupi/ web/ webpack.config.js
  • 35. DIST_DIR=$(pwd)/dist mkdir -p "${DIST_DIR}" composer install --no-interaction --no-dev --optimize-autoloader --profile npm ci npm run build # custom commands S_CONSOLE="php -d memory_limit=2048M ./app/${APP_NAME}/console" ${S_CONSOLE} cache:clear --no-warmup --env=prod --no-debug ${S_CONSOLE} cache:warmup --env=prod --no-debug ${S_CONSOLE} assets:install --env=prod --no-debug ./web/"${RELEASE_VERSION}$ {RELEASE_SUBVERSION}" ${S_CONSOLE} company:asset-dump --env=prod --no-debug ${S_CONSOLE} company:container-dump --env=prod --no-debug # remove stuff we don't want in our packages' rm web/*_dev.php web/*_integration.php web/*_jenkins.php rm -rf node_modules test-js test test-trupi # package PHP tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf "${DIST_DIR}/${PACKAGE_ID}.tar.gz" * # package PHP cache pushd /var/www/prod find ${PACKAGE_ID} -type d -exec chmod 777 {} + tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf "${DIST_DIR}/${PACKAGE_ID}.cache.tar.gz" ${PACKAGE_ID} popd # package assets cd ./web tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf “${DIST_DIR}/${PACKAGE_ID}.assets.tar.gz" v* How do you build that? $ ls CHANGELOG.md Dockerfile Patches/ README.md app/ bin/ composer.json composer.lock package-lock.json package.json php72-compatibility-fix.patch postcss.config.js sonar-project.properties src/ src-js/ test/ test-trupi/ web/ webpack.config.js
  • 36. DIST_DIR=$(pwd)/dist mkdir -p "${DIST_DIR}" composer install --no-interaction --no-dev --optimize-autoloader --profile npm ci npm run build # custom commands S_CONSOLE="php -d memory_limit=2048M ./app/${APP_NAME}/console" ${S_CONSOLE} cache:clear --no-warmup --env=prod --no-debug ${S_CONSOLE} cache:warmup --env=prod --no-debug ${S_CONSOLE} assets:install --env=prod --no-debug ./web/"${RELEASE_VERSION}$ {RELEASE_SUBVERSION}" ${S_CONSOLE} company:asset-dump --env=prod --no-debug ${S_CONSOLE} company:container-dump --env=prod --no-debug # remove stuff we don't want in our packages' rm web/*_dev.php web/*_integration.php web/*_jenkins.php rm -rf node_modules test-js test test-trupi # package PHP tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf "${DIST_DIR}/${PACKAGE_ID}.tar.gz" * # package PHP cache pushd /var/www/prod find ${PACKAGE_ID} -type d -exec chmod 777 {} + tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf "${DIST_DIR}/${PACKAGE_ID}.cache.tar.gz" ${PACKAGE_ID} popd # package assets cd ./web tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf “${DIST_DIR}/${PACKAGE_ID}.assets.tar.gz" v* How do you build that? Obviously 🙄 $ ls CHANGELOG.md Dockerfile Patches/ README.md app/ bin/ composer.json composer.lock package-lock.json package.json php72-compatibility-fix.patch postcss.config.js sonar-project.properties src/ src-js/ test/ test-trupi/ web/ webpack.config.js
  • 37. How do you deploy that? $ ls CHANGELOG.md Dockerfile Patches/ README.md app/ bin/ composer.json composer.lock package-lock.json package.json php72-compatibility-fix.patch postcss.config.js sonar-project.properties src/ src-js/ test/ test-trupi/ web/ webpack.config.js
  • 38. How do you deploy that? Good luck with that 🍀 $ ls CHANGELOG.md Dockerfile Patches/ README.md app/ bin/ composer.json composer.lock package-lock.json package.json php72-compatibility-fix.patch postcss.config.js sonar-project.properties src/ src-js/ test/ test-trupi/ web/ webpack.config.js
  • 39. So how do we solve that?
  • 41. README Everybody can read it, many won’t
  • 46. CI/CD pipeline continuous delivery for free* * not really
  • 48. CI/CD pipeline everybody can use it* * if they know the URLs
  • 52. language specific tools OK for beginners
  • 53. language specific tools and then you add a 2nd language
  • 57. Use a language agnostic build tool
  • 59. CMake, redo, Gradle, Meson, SCons, Ninja, ant, Bazel, tup, …
  • 60. CMake, redo, Gradle, Meson, SCons, Ninja, ant, Bazel, tup, … ✋
  • 61. Let’s start with a simple build.sh
  • 62. #!/bin/bash composer install --no-interaction --no-dev npm ci npm run build tar czf dist/$1.tar.gz web/
  • 63. #!/bin/bash composer install --no-interaction --no-dev npm ci npm run build tar czf dist/$1.tar.gz web/ no -x
  • 64. #!/bin/bash composer install --no-interaction --no-dev npm ci npm run build tar czf dist/$1.tar.gz web/ no -e
  • 65. #!/bin/bash composer install --no-interaction --no-dev npm ci npm run build tar czf dist/$1.tar.gz web/ no tests
  • 66. #!/bin/bash composer install --no-interaction --no-dev npm ci npm run build tar czf dist/$1.tar.gz web/ needs an argument
  • 67. #!/bin/bash composer install --no-interaction --no-dev npm ci npm run build tar czf dist/$1.tar.gz web/ needs an argument weird issues
  • 69. #!/usr/bin/env bash set -ex if [ -n "$1" ]; then echo "PACKAGE_ID missing" return 1 else export PACKAGE_ID="$1" fi composer install --no-interaction --no-dev composer test npm ci npm run build npm test tar czf dist/"$PACKAGE_ID".tar.gz web/ vendor/
  • 70. #!/usr/bin/env bash set -ex if [ -n "$1" ]; then echo "PACKAGE_ID missing" return 1 else export PACKAGE_ID="$1" fi composer install --no-interaction --no-dev composer test npm ci npm run build npm test tar czf dist/"$PACKAGE_ID".tar.gz web/ vendor/ a bit verbose
  • 71. not simple to just run the tests #!/usr/bin/env bash set -ex if [ -n "$1" ]; then echo "PACKAGE_ID missing" return 1 else export PACKAGE_ID="$1" fi composer install --no-interaction --no-dev composer test npm ci npm run build npm test tar czf dist/"$PACKAGE_ID".tar.gz web/ vendor/
  • 72. re-installs all npm packages #!/usr/bin/env bash set -ex if [ -n "$1" ]; then echo "PACKAGE_ID missing" return 1 else export PACKAGE_ID="$1" fi composer install --no-interaction --no-dev composer test npm ci npm run build npm test tar czf dist/"$PACKAGE_ID".tar.gz web/ vendor/
  • 73. more and more cases #!/usr/bin/env bash set -ex if [ -n "$1" ]; then echo "PACKAGE_ID missing" return 1 else export PACKAGE_ID="$1" fi composer install --no-interaction --no-dev composer test npm ci npm run build npm test tar czf dist/"$PACKAGE_ID".tar.gz web/ vendor/
  • 74. more and more scripts #!/usr/bin/env bash set -ex if [ -n "$1" ]; then echo "PACKAGE_ID missing" return 1 else export PACKAGE_ID="$1" fi composer install --no-interaction --no-dev composer test npm ci npm run build npm test tar czf dist/"$PACKAGE_ID".tar.gz web/ vendor/
  • 75. good luck 🍀 #!/usr/bin/env bash set -ex if [ -n "$1" ]; then echo "PACKAGE_ID missing" return 1 else export PACKAGE_ID="$1" fi composer install --no-interaction --no-dev composer test npm ci npm run build npm test tar czf dist/"$PACKAGE_ID".tar.gz web/ vendor/
  • 76. Makefiles to the rescue!
  • 78. But why Makefiles? execute steps in the shell
  • 79. But why Makefiles? we already have most of that
  • 81. But why Makefiles? -ex for free* * for real this time
  • 82. But why Makefiles? installed virtually everywhere
  • 84. But why Makefiles? file based dependencies
  • 85. But why Makefiles? handbook of common tasks
  • 86. But why Makefiles? very useful at 1 AM 🕐
  • 87. But why Makefiles? or with a new puppy 🐶
  • 88. How would that look like?
  • 89. test: dependencies composer test npm test # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/ dependencies: composer install --no-interaction --no-dev npm install .PHONY: test build dependencies
  • 90. test: dependencies composer test npm test # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/ dependencies: composer install --no-interaction --no-dev npm install .PHONY: test build dependencies test: dependencies composer test npm test
  • 91. test: dependencies composer test npm test # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/ dependencies: composer install --no-interaction --no-dev npm install .PHONY: test build dependencies # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/
  • 92. test: dependencies composer test npm test # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/ dependencies: composer install --no-interaction --no-dev npm install .PHONY: test build dependencies dependencies: composer install --no-interaction --no-dev npm install
  • 93. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install
  • 94. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test
  • 95. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js # alias for the actual Artifact build: dist/my_project.tar.gz dist/my_project.tar.gz: npm run build mkdir -p dist/ tar czf dist/my_project.tar.gz web/ vendor/
  • 96. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
  • 97. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/ $ make build PACKAGE_ID=my_package-1.2.9
  • 98. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js $ make build # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
  • 99. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
  • 100. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install # guard against being called without parameters # or environment variables env-%: test -n "${$*}"  (echo "Argument $* not set"; exit 1) .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js env-% dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID […] # guard against being called without parameters # or environment variables env-%: test -n "${$*}"  (echo "Argument $* not set"; exit 1)
  • 101. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID npm run build mkdir -p dist/ tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install # guard against being called without parameters # or environment variables env-%: @test -n "${$*}"  (echo "Argument $* not set"; exit 1) .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js env-% # guard against being called without parameters # or environment variables env-%: @test -n "${$*}"  (echo "Argument $* not set"; exit 1)
  • 102. test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID npm run build mkdir -p dist/ tar czf $@ web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install # guard against being called without parameters # or environment variables env-%: @test -n "${$*}"  (echo "Argument $* not set"; exit 1) .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js env-% dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID npm run build mkdir -p dist/ tar czf $@ web/ vendor/
  • 103. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := "my_package-${VERSION}"
  • 104. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := "my_package-${VERSION}"
  • 105. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" DOCKER_REGISTRY := "docker.company.com/" DOCKER_NAME := "my-project:${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz docker: dist/${PACKAGE_ID}.tar.gz docker build --build-arg PACKAGE=$< -t "${DOCKER_REGISTRY}${DOCKER_NAME}" ./ push: docker docker push "${DOCKER_REGISTRY}${DOCKER_NAME}" dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push DOCKER_REGISTRY := "docker.company.com/" DOCKER_NAME := “my-project:${VERSION}" docker: dist/${PACKAGE_ID}.tar.gz docker build --build-arg PACKAGE=$< -t "${DOCKER_REGISTRY}${DOCKER_NAME}" ./ push: docker docker push "${DOCKER_REGISTRY}${DOCKER_NAME}"
  • 106. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" DOCKER_REGISTRY := "docker.company.com/" DOCKER_NAME := "my-project:${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz docker: dist/${PACKAGE_ID}.tar.gz docker build --build-arg PACKAGE=$< -t "${DOCKER_REGISTRY}${DOCKER_NAME}" ./ push: docker docker push "${DOCKER_REGISTRY}${DOCKER_NAME}" deploy: push kubectl apply -f k8s/deployment.yaml dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push deploy: push kubectl apply -f k8s/deployment.yaml
  • 107. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" DOCKER_REGISTRY := "docker.company.com/" DOCKER_NAME := "my-project:${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz docker: dist/${PACKAGE_ID}.tar.gz docker build --build-arg PACKAGE=$< -t "${DOCKER_REGISTRY}${DOCKER_NAME}" ./ push: docker docker push "${DOCKER_REGISTRY}${DOCKER_NAME}" deploy: k8s/deployment.yaml push kubectl apply -f $< dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ k8s/deployment.yaml: k8s/deployment.yaml.template sed 's#my-project:VERSION#${DOCKER_NAME}#' > $@ dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml deploy: k8s/deployment.yaml push kubectl apply -f $< k8s/deployment.yaml: k8s/deployment.yaml.template sed 's#my-project:VERSION#${DOCKER_NAME}#' > $@
  • 108. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" DOCKER_REGISTRY := "docker.company.com/" DOCKER_NAME := "my-project:${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz docker: dist/${PACKAGE_ID}.tar.gz docker build --build-arg PACKAGE=$< -t "${DOCKER_REGISTRY}${DOCKER_NAME}" ./ push: docker docker push "${DOCKER_REGISTRY}${DOCKER_NAME}" deploy: k8s/deployment.yaml push kubectl apply -f $< dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ k8s/deployment.yaml: k8s/deployment.yaml.template sed 's#my-project:VERSION#${DOCKER_NAME}#' > $@ clean: rm -rf ./vendor/ # php packages npm clean rm k8s/deployment.yaml distclean: rm dist/*.tar.gz dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml clean distclean clean: rm -rf ./vendor/ # php packages npm clean rm k8s/deployment.yaml distclean: rm dist/*.tar.gz
  • 109. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" DOCKER_REGISTRY := "docker.company.com/" DOCKER_NAME := "my-project:${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz docker: dist/${PACKAGE_ID}.tar.gz docker build --build-arg PACKAGE=$< -t "${DOCKER_REGISTRY}${DOCKER_NAME}" ./ push: docker docker push "${DOCKER_REGISTRY}${DOCKER_NAME}" deploy: k8s/deployment.yaml push kubectl apply -f $< dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ k8s/deployment.yaml: k8s/deployment.yaml.template sed 's#my-project:VERSION#${DOCKER_NAME}#' > $@ clean: rm -rf ./vendor/ # php packages npm clean rm k8s/deployment.yaml distclean: rm dist/*.tar.gz dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml clean distclean In your CI/CD system: $ make test deploy
  • 110. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" DOCKER_REGISTRY := "docker.company.com/" DOCKER_NAME := "my-project:${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz docker: dist/${PACKAGE_ID}.tar.gz docker build --build-arg PACKAGE=$< -t "${DOCKER_REGISTRY}${DOCKER_NAME}" ./ push: docker docker push "${DOCKER_REGISTRY}${DOCKER_NAME}" deploy: k8s/deployment.yaml push kubectl apply -f $< dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ k8s/deployment.yaml: k8s/deployment.yaml.template sed 's#my-project:VERSION#${DOCKER_NAME}#' > $@ clean: rm -rf ./vendor/ # php packages npm clean rm k8s/deployment.yaml distclean: rm dist/*.tar.gz dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml clean distclean On your development system: $ make test
  • 111. VERSION := $(shell git rev-parse --short HEAD) PACKAGE_ID := “my_package-${VERSION}" DOCKER_REGISTRY := "docker.company.com/" DOCKER_NAME := "my-project:${VERSION}" test: test-php test-js test-php: dependencies-php composer test test-js: dependencies-js npm test # alias for the actual Artifact build: dist/${PACKAGE_ID}.tar.gz docker: dist/${PACKAGE_ID}.tar.gz docker build --build-arg PACKAGE=$< -t "${DOCKER_REGISTRY}${DOCKER_NAME}" ./ push: docker docker push "${DOCKER_REGISTRY}${DOCKER_NAME}" deploy: k8s/deployment.yaml push kubectl apply -f $< dist/${PACKAGE_ID}.tar.gz: npm run build mkdir -p dist/ tar czf $@ web/ vendor/ k8s/deployment.yaml: k8s/deployment.yaml.template sed 's#my-project:VERSION#${DOCKER_NAME}#' > $@ clean: rm -rf ./vendor/ # php packages npm clean rm k8s/deployment.yaml distclean: rm dist/*.tar.gz dependencies: dependencies-php dependencies-js dependencies-php: composer install --no-interaction --no-dev dependencies-js: npm install .PHONY: test test-php test-js build dependencies dependencies-php dependencies-js docker push k8s/deployment.yaml clean distclean At 1 AM 🕐 to release a fix 🚒: $ make deploy
  • 115. common Makefile targets libraries applications docker-based projects all ✅ ✅ ✅ build ✅ ✅ ✅ debug ✅ ✅ ✅ test ✅ ✅ ✅ release ✅ ✅ push ✅ deploy ✅ ✅ clean ✅ ✅ ✅ distclean ✅ ✅ install ✅ up ✅ see make manual — 16.6 Standard Targets for Users
  • 116. - FOO = "value"
 The value is recursively computed every time FOO is being accessed. - FOO := "value"
 The value is directly computed and assigned once to FOO. - FOO ?= "value"
 The value is lazily set if FOO has no value at the moment it is being accessed. - FOO += "value"
 The value is appended to FOO with a space in front. see make manual — 6.2 The Two Flavors of Variables The different kinds of assignments
  • 117. As an argument (better): $ make target FOO=bar As an environment variable (requires ?= in Makefile): $ FOO=bar make target Setting variables on make invocation see make manual — 9.5 Overriding Variables
  • 118. Dos - Fan out from general targets to more specific ones: deploy " deploy-nomad, not deploy- nomad " deploy TOOL=nomad, use pattern rules to accomplish this with similar targets. - Split up big targets into smaller chunks; that gives the user the possibility to not have to run everything but only the parts the they are interested in: test " test-unit, test-integration, test-codestyle, then the user can just call make test-unit. - Name targets after files where-ever possible. It will reduce the amount of unnecessary computation for invocations. - Use variables to abstract common command calls. ${MAKE} and ${CC} are standards. Why not add ${GREP} or ${DOCKER}, so users can point your Makefile to the binary they want to use for those programs?
  • 119. Don’ts - Use :: to define phony rules. That’s just a side-effect of their main purpose (to split one target over multiple Makefiles). - Add to many targets that aren’t used on a regular basis. They will become stale without you noticing.