nystudio107.com /blog/using-make-makefiles-to-automate-your-frontend-workflow

Using Make & Makefiles to Automate your Frontend Workflow

12-15 minutes

Andrew Welch · Insights · #docker #webpack #node.js

Mak­ing the web bet­ter one site at a time, with a focus on per­for­mance, usabil­i­ty & SEO

Published , updated · 5 min read · RSS Feed


Please consider 🎗 sponsoring me 🎗 to keep writing articles like this.

Make is a build automa­tion tool that’s been used for decades to build soft­ware. Learn how you can lever­age make to auto­mate fron­tend web development

Make makefile

make is a Unix tool that’s been around since the mid 1970’s, and is still wide­ly used today for automat­ing soft­ware build processes.

make is the OG of build tools

The make tool is avail­able for just about every plat­form you can imag­ine, and is installed with the XCode CLI Tools on the Mac, and WSL2 on Win­dows. Prob­a­bly you have these installed already if you’re doing development.

This arti­cle describes how you can lever­age make to auto­mate your fron­tend web devel­op­ment via a stan­dard­ized CLI API of your own design.

Link Why use make?

With all of the build tools and auto­mat­ed sys­tems out there, why should we use make? A few reasons:

  • It lets you define a sim­ple, stan­dard­ized CLI API your team can use across projects
  • The verb-noun seman­tics of make target are easy to digest & remember
  • You effec­tive­ly get local com­mand alias­es for each project that are con­text and project aware
  • The tool­ing under the hood is abstract­ed away, and can be swapped out at any time
  • make is already installed on your devel­op­ment machines, and is designed to auto­mate builds

So peo­ple on your team (or just you, if you’re a team of one) can just type make dev to spin up a local devel­op­ment envi­ron­ment, with­out hav­ing to know what hap­pens under the hood to make that happen.

If you decide to use a dif­fer­ent local dev envi­ron­ment, you can swap it out, and make dev will still do the thing” to make it happen.

Abstracting away what does the thing from the command has many benefits

So here’s an exam­ple of some of the com­mands I have in my Makefile, and what they do:

  • make dev — does what­ev­er needs to be done to spin up the pro­jec­t’s local dev envi­ron­ment, so I can work on the project
  • make build — does what­ev­er needs to be done to build a pro­jec­t’s pro­duc­tion ready resources for deployment
  • make clean — does what­ev­er needs to be done to rebuild the project envi­ron­ment from scratch
  • make docs — for my plu­g­ins, does what­ev­er needs to be done to build the documentation

There are more com­mands of course, but these are a few exam­ples that show how easy it is to onboard some­one onto a project.

For a real-world exam­ple, try spin­ning up the dev​Mode​.fm web­site locally!

Link How make works

When you run make via your CLI ter­mi­nal, it might look some­thing like this:

make looks for a plain text file named Makefile in the cur­rent direc­to­ry. This file has any num­ber of tar­gets that look like this:

Anato­my of a Make­file rule

  • Tar­get — this is nor­mal­ly a file or direc­to­ry that needs to be built.
  • Pre­req­ui­sites — oth­er files or tar­gets that need to be built before the tar­get can be built.
  • Recipe — pre­ced­ed by a tab, a series of any num­ber of shell com­mands that are exe­cut­ed to build the target

So in the above exam­ple when we type make build it will first do what­ev­er is need­ed to build the tar­get named up (which is a pre­req­ui­site), and then it’ll run the com­mands in the recipe to build the tar­get named build.

make will rebuild a tar­get when either the tar­get file or direc­to­ry does­n’t exist, or when any of its pre­req­ui­sites have been mod­i­fied and so are new­er than the target.

This Make­file Cheat­sheet may come in handy when learn­ing how Make­files work.

Anato­my of a Make­file preamble

Using make, we can get local aliases to run project-specific commands

We men­tioned ear­li­er that tar­gets are nor­mal­ly files or direc­to­ries, but we can use the spe­cial built-in tar­get named .PHONY to spec­i­fy that the tar­get is just a list of com­mands that should always be run.

We lever­age this to use our Makefile to define local alias­es that run com­mands to do var­i­ous things with our project.

So let’s have a look at a few examples.

Link Craft Scaffolding Makefile

This Make­file is one I use in my Craft CMS scaf­fold­ing. The CMS or frame­work in use does­n’t real­ly mat­ter, the applied prin­ci­ples are what is important.

CONTAINER?=$(shell basename $(CURDIR))_php_1
BUILDCHAIN?=$(shell basename $(CURDIR))_webpack_1

.PHONY: build clean composer dev npm pulldb restoredb up

build: up
	docker exec -it ${BUILDCHAIN} npm run build
clean:
	docker-compose down -v
	docker-compose up --build
composer: up
	docker exec -it ${CONTAINER} composer \
		$(filter-out $@,$(MAKECMDGOALS))
craft: up
	docker exec -it ${CONTAINER} php craft \
		$(filter-out $@,$(MAKECMDGOALS))
dev: up
npm: up
	docker exec -it ${BUILDCHAIN} npm \
		$(filter-out $@,$(MAKECMDGOALS))
pulldb: up
	cd scripts/ && ./docker_pull_db.sh
restoredb: up
	cd scripts/ && ./docker_restore_db.sh \
		$(filter-out $@,$(MAKECMDGOALS))
update:
	docker-compose down
	rm -f cms/composer.lock
	rm -f buildchain/package-lock.json
	docker-compose up
update-clean:
	docker-compose down
	rm -f cms/composer.lock
	rm -rf cms/vendor/
	rm -f buildchain/package-lock.json
	rm -rf buildchain/node_modules/
	docker-compose up
up:
	if [ ! "$$(docker ps -q -f name=${CONTAINER})" ]; then \
        docker-compose up; \
    fi
%:
	@:
# ref: https://stackoverflow.com/questions/6273608/how-to-pass-argument-to-makefile-from-command-line

The key com­mands are:

  • make dev — brings up the local devel­op­ment envi­ron­ment; in this case that means spin­ning up the Dock­er con­tain­ers via docker-compose up
  • make build — exe­cutes a fron­tend build­chain build to cre­ate the pro­duc­tion resources, by run­ning npm run build in the build­chain Dock­er container
  • make npm xxx — runs the passed in NPM com­mand in the build­chain Dock­er container
  • make composer xxx — runs the passed in Com­pos­er com­mand in the PHP Dock­er container
  • make craft xxx — runs the passed in Craft CLI com­mand in the PHP Dock­er container

If I ever changed local devel­op­ment envi­ron­ments or fron­tend build­chains, I would­n’t need to re-edu­cat­ed my team (or re-train myself) on the new commands.

I can just swap in the machin­ery need­ed to do the thing in the Makefile.

Tip: If you try a com­mand like make craft project-config/apply --force you’ll see an error, because the shell thinks the --force flag should be applied to the make com­mand. To side-step this, use the -- (dou­ble-dash) to dis­able fur­ther option pro­cess­ing, like this: make -- craft project-config/apply --force

Link Craft Plugin Development Environment Makefile

This is the Make­file from my Craft CMS Plu­g­in devel­op­ment envi­ron­ment.

It’s a skele­ton project I use to build and test my Craft CMS plu­g­ins, with some spe­cif­ic func­tion­al­i­ty to make that easy.

CONTAINER?=$(shell basename $(CURDIR))_php_1

.PHONY: dev clean composer mysql postgres up

dev: up
clean:
	docker-compose down -v
	docker-compose up --build
composer: up
	docker exec -it ${CONTAINER} composer \
		$(filter-out $@,$(MAKECMDGOALS))
craft: up
	docker exec -it ${CONTAINER} php craft \
		$(filter-out $@,$(MAKECMDGOALS))
mysql: up
	cp cms/config/_dbconfigs/mysql.php cms/config/db.php
postgres: up
	cp cms/config/_dbconfigs/postgres.php cms/config/db.php
update:
	docker-compose down
	rm -f cms/composer.lock
	docker-compose up
update-clean:
	docker-compose down
	rm -f cms/composer.lock
	rm -rf cms/vendor/
	docker-compose up
up:
	if [ ! "$$(docker ps -q -f name=${CONTAINER})" ]; then \
        docker-compose up; \
    fi
%:
	@:
# ref: https://stackoverflow.com/questions/6273608/how-to-pass-argument-to-makefile-from-command-line

The key com­mands are:

  • make dev — brings up the local devel­op­ment envi­ron­ment; in this case that means spin­ning up the Dock­er con­tain­ers via docker-compose up
  • make composer xxx — runs the passed in Com­pos­er com­mand in the PHP Dock­er container
  • make craft xxx — runs the passed in Craft CLI com­mand in the PHP Dock­er container
  • make mysql — dynam­i­cal­ly switch­es the project over to using the MySQL data­base container
  • make postgres — dynam­i­cal­ly switch­es the project over to using the Post­gres data­base container

This lets me repli­cate my plu­g­in devel­op­ment envi­ron­ment on any machine eas­i­ly, and test against var­i­ous sce­nar­ios (such as mul­ti­ple data­base types) just as easily.

TAG?=14-alpine
CONTAINER?=$(shell basename $(CURDIR))-buildchain
DOCKERRUN=docker container run \
	--name ${CONTAINER} \
	--rm \
	-t \
	--network plugindev_default \
	-p 8080:8080 \
	-v "${CURDIR}":/app \
	${CONTAINER}:${TAG}
DOCSDEST?=../../sites/nystudio107/web/docs/retour

.PHONY: build dev docker docs install npm

build: docker install
	${DOCKERRUN} \
		run build
dev: docker install
	${DOCKERRUN} \
		run dev
docker:
	docker build \
		. \
		-t ${CONTAINER}:${TAG} \
		--build-arg TAG=${TAG} \
		--no-cache
docs: docker
	${DOCKERRUN} \
		run docs
	rm -rf ${DOCSDEST}
	mv ./docs/docs/.vuepress/dist ${DOCSDEST}
install: docker
	${DOCKERRUN} \
		install
update: docker
	rm -f buildchain/package-lock.json
	${DOCKERRUN} \
		install
update-clean: docker
	rm -f buildchain/package-lock.json
	rm -rf buildchain/node_modules/
	${DOCKERRUN} \
		install
npm: docker
	${DOCKERRUN} \
		$(filter-out $@,$(MAKECMDGOALS))
%:
	@:
# ref: https://stackoverflow.com/questions/6273608/how-to-pass-argument-to-makefile-from-command-line

The key com­mands are:

  • make dev — brings up the local devel­op­ment envi­ron­ment; in this case that means spin­ning up the Dock­er con­tain­er for the buildchain
  • make build — exe­cutes a fron­tend build­chain build to cre­ate the pro­duc­tion resources, by run­ning npm run build in the build­chain Dock­er container
  • make npm xxx — runs the passed in NPM com­mand in the build­chain Dock­er container
  • make docs — builds the doc­u­men­ta­tion for the plu­g­in via npm run docs in the build­chain Dock­er container

As you can see, some of the core com­mands remain the same amongst the var­i­ous projects, despite the under­ly­ing machin­ery being quite different.

Link One Makefile to Rule Them All

Some­times it’s con­ve­nient to be able to run a whole lot of builds with one command.

For exam­ple, when there’s a depend­abot-report­ed secu­ri­ty vul­ner­a­bil­i­ty in the build­chain that’s used in Vue­Press (which is what I use for my doc­u­men­ta­tion), and I want to rebuild all of my Craft CMS Plu­g­in doc­u­men­ta­tion at once.

This Makefile search­es through all sub-direc­to­ries below it for oth­er Makefiles to run (ignor­ing node_modules/ and vendor/) :

MAKEFILES:=$(shell find . -mindepth 2 -type d \( -name node_modules -o -name vendor \) -prune -false -o -type f \( -name 'GNUmakefile' -o -name 'makefile' -o -name 'Makefile' \))
SUBDIRS:=$(foreach m,$(MAKEFILES),$(realpath $(dir $(m))))

$(MAKECMDGOALS): $(SUBDIRS)
$(SUBDIRS):
	$(MAKE) -C $@ $(MAKECMDGOALS)

.PHONY: $(MAKECMDGOALS) $(SUBDIRS)

So at the root lev­el of my Craft CMS Plu­g­ins devel­op­ment direc­to­ry, I can type just make docs and it’ll run make docs for every sub-direc­to­ry that has a Makefile.

This gives me the best of both worlds in terms of keep­ing every­thing in sep­a­rate repos­i­to­ries (maybe with sep­a­rate semver require­ments, or even total­ly dif­fer­ent doc­u­men­ta­tion build sys­tems), but also being able to rebuild every­thing in one fell swoop.

Link Shell aliases in Makefiles

Shell alias­es won’t work in Make­files, because by default, most shells do not eval­u­ate alias­es when they are non-inter­ac­tive shells.

If you use shell alias­es often, such as I talk about in the Dock Life: Using Dock­er for All The Things! arti­cle, this is a bummer.

But there’s a pret­ty easy work-around. You can just assign a Make­file vari­able to the alias, sourced from your rc file, and use that:

COMPOSER=$(shell grep alias\ composer= ~/.zshrc | awk -F"'" '{print $$2}')

What this does is it looks in the shel­l’s rc file (~/.zshrc in this case, but it could be ~/.bashrc if you’re using the Bash shell) for the text alias composer= and extracts the result into the COMPOSER Make­file variable.

Then you can use it in your Make­file like this:

Don’t wor­ry, the glob­al alias­es you’ve defined won’t over­ride the local alias­es you’ve set up in your Makefiles.

Link Old School Cool

When­ev­er you’re look­ing for a solu­tion, try to lever­age work that’s already been done by some­one else.

make has been around for a long time, but it’s also proven itself to be extreme­ly use­ful in automat­ing build processes.

Some­times old school cool is just what you need.

Hap­py making!

::...
免责声明:
当前网页内容, 由 大妈 ZoomQuiet 使用工具: ScrapBook :: Firefox Extension 人工从互联网中收集并分享;
内容版权归原作者所有;
本人对内容的有效性/合法性不承担任何强制性责任.
若有不妥, 欢迎评注提醒:

或是邮件反馈可也:
askdama[AT]googlegroups.com


订阅 substack 体验古早写作:


点击注册~> 获得 100$ 体验券: DigitalOcean Referral Badge

关注公众号, 持续获得相关各种嗯哼:
zoomquiet


自怼圈/年度番新

DU22.4
关于 ~ DebugUself with DAMA ;-)
粤ICP备18025058号-1
公安备案号: 44049002000656 ...::