A Makefile provides a simple entry point for any project for scripts and utilities, regardless of the language or platform. And there are a few tips I can offer to make yours more useful. In short, for a Javascript project or Java project, make can easily run npm
or mvn
or gradle
, as well as assist in routine operations. And while I’ll still find myself running mvn package
or npm t
, here are some invaluable use cases:
- Building artifacts
- Running tests with profiles
- Running specific tests by classname
- Building docker images
- Helm deployment to Kubernetes or VMs
New to Make?
If you haven’t used make
before, its very easy to get started. There are just a few things to know, and you’ll be off to the races:
make
is generally available in every operating system, so you needn’t install anything.- Comments start with
#
- Makefiles are structured as follows, defining the target and the commands with whitespace:
my-target: @echo "This prints text, but doesn't show the command because of the @ symbol" echo "This prints text, and shows the command that executed." npm run build
- You can also set other commands as prerequisites by including them, in order, after the colon:
deploy: clean build ENV=production HOST=bravo.clearthehaze.com npm run deploy-to-box
Let’s Get Awesome
Now, if you want to make your Makefile really spectacular,
Use Colors!
Since terminals support color, spruce up your output with some color, using these variables in your Makefile, and run with make color-demo
:
color-demo: @echo "$(BLACK)Black$(BLUE)" @echo "$(GREEN)Green$(BLUE)" @echo "$(RED)Red$(BLUE)" @echo "$(YELLOW)Yellow$(BLUE)" @echo "$(BLUE)Blue$(BLUE)" @echo "$(MAGENTA)Magenta$(BLUE)" @echo "$(CYAN)Cyan$(BLUE)" @echo "$(WHITE)White$(BLUE)" ################################### ## Variables for Makefile ################################### # Colors BLUE=\x1b[0m BLACK=\x1b[30;01m RED=\x1b[31;01m GREEN=\x1b[32;01m YELLOW=\x1b[33;01m BLUE=\x1b[34;01m MAGENTA=\x1b[35;01m CYAN=\x1b[36;01m WHITE=\x1b[37;01m
Just remember to reset the color, else all of your output will change.
Include A Help Section
I always add this target to give explicit guidance and documentation on how to use a Makefile, as the maturity of products vary over time and I may not the implementation of new patterns. And if it is your first target, then it will be printed automatically when make is run.
I like to divide up commands to help local development from production operations, and color code them accordingly.
help: @echo "You have the following options when using this Makefile:" @echo @echo "$(YELLOW)Development related:" @echo "$(YELLOW) package $(BLUE)- builds the artifacts $(BLUE)make package" @echo "$(YELLOW) test-class $(BLUE)- runs a single test, identified by setting CLASS, eg. $(BLUE)make test-class CLASS=StringUtils" @echo "$(YELLOW) docker $(BLUE)- builds the docker image, including tag $(CODE_COLOR)make docker TAG=0.0.11" @echo "$(YELLOW) run-container $(BLUE)- runs the built docker container, eg. $(CODE_COLOR)make run-container TAG=0.0.11" @echo "$(YELLOW) stop-container $(BLUE)- stops the running docker container, eg. $(CODE_COLOR)make stop-container" @echo @echo "$(GREEN)Production related:" @echo "$(GREEN) deploy-production $(BLUE)- deploys to production $(CODE_COLOR)make deploy-production IMAGE=<url>" @echo "$(GREEN) restart-production $(BLUE)- restarts the production service $(CODE_COLOR)make restart-production" @echo "$(GREEN) canary $(BLUE)- runs the canary health check on production $(BLUE)make rolling-restart-prod-phx" @echo "$(BLUE)"
Use Default Variables
Scripts are intended to simplify the routine operations for a service. That implies consistent values used in commands, such as the service name or a data center for hosting. Variables allow you to set a project specific value for an otherwise common command you’d use in any project. These variables, and your standard environment variables, can be used as ${MY_VAR}
in commands, or $(MY_VAR)
in echo statements.
################################### ## Default values for all variables ################################### ENV ?= test # Kubernetes deployment variables CLUSTER ?= us-east-1 NAMESPACE ?= cth-test DOCKER_REGISTRY ?= https://hub.docker.io/clearthehaze/ SERVICE_NAME ?= clearthehaze-site TAG ?= latest
And with those variables ready…
Simplify Deployments
Ideally, all deployments are going through CI/CD. But it is handy to know how to manage your service and handle deployments for feature testing, production support, etc. Deployment commands tend to be routine with little variance between versions, and a Makefile is a handy way to document those commands.
Here is an example with Helm, including variables set above:
# This command will deploy to Kubernetes. If TAG is not provided, will deploy latest# make deploy TAG=cc2c8f3ad3087e9793c49e4e2361d686b49fab20 deploy: @echo"$(GREEN)Deploying $(SERVICE_NAME) to $(ENV)$(NO_COLOR)" @echo"Helm output..." helm template kubernetes -f kubernetes/${ENV}.yaml --set image=${DOCKER_REGISTRY}${SERVICE_NAME}:${TAG} @echo"Running helm again and sending to kubernetes" helm template kubernetes -f kubernetes/${ENV}.yaml --set image=${DOCKER_REGISTRY}${SERVICE_NAME}:${TAG} | kubectl --cluster ${CLUSTER} -n ${NAMESPACE} apply -f - kubectl --cluster ${CLUSTER} -n ${NAMESPACE} rollout status deployment/${SERVICE_NAME}
Hello. This post couldn’t be written any better! Reading this post reminds me of my previous room mate. He always kept chatting about this. I will forward this page to him. Fairly certain he will have a good read. Thank you for sharing.