Makefile Application Presets
21 May 2020
We saw in the last post how to use Makefile wildcards to write targets like this:
migrate-to-%:
@bundle exec rake migrate[$(*)]
guard-%:
@if [ -z '${${*}}' ]; then echo 'ERROR: variable $* not set' && exit 1; fi
logs: guard-STACK
@awslogs get -w /ecs/$(STACK)_MyService
So that we can build CLIs like this:
$ make migrate-to-50
Migrating to version 50...
$ make logs
ERROR: variable STACK not set
$ STACK=qa make logs
...
(Note the @
prefix on commands in the Makefile, it avoids the line being echoed to stdout)
This is neat but it only works well for user-supplied values, like "50"
.
There are cases where we want the user to supply an argument, but not the value.
Say, for example, users want to specify 'production' to 'staging'
but we don't want them to remember the URL to the server.
We can use wildcards to dynamically select a Make variable:
staging_url:=https://staging-api.lithic.tech
production_url:=https://api.lithic.tech
ping-%:
curl "$($(*)_url)"
And we can use it as so:
$ make ping-staging
curl "https://staging-api.lithic.tech"
Okay, this example isn't incredibly useful. But for some clients, we have multiple deployed versions of the same application, and we can use these variables to avoid having to remember where applications are deployed.
For example, let's say we have 3 versions of a codebase deployed in Heroku: one staging and two production apps.
In the Make snippet below, each _app
variable refers to the name of a Heroku app.
We can use that app name to get the database string using the Heroku CLI,
and pass that to psql
(Postgres CLI).
staging_app:=lithic-api-staging
production-pdx_app:=lithic-api-production
production-nyc_app:=lithic-api-production-nyc
psql-%:
psql `heroku config:get DATABASE_URL --app=$($(*)_app)`
Now to connect to staging, it's as simple as:
$ make psql-staging
If we use Heroku's Review Apps, we should also support an environment-variable version of these sorts of commands,
since the app names are ephemeral. Instead of a wildcard, we'll require the APP
environment variable is set:
psql-app: guard-APP
psql `heroku config:get DATABASE_URL --app=${APP}`
Putting It All Together
The example above has a couple small error cases that may confuse users:
if psql
or heroku
are not on the PATH
,
the command will error with a sort of cryptic error:
$ make psql-staging
psql `heroku config:get DATABASE_URL --app=lithic-api-staging`
/bin/sh: heroku: command not found
could not connect to server: No such file or directory
Yuck! Can we use something like our lovely guard-%
target to declare our executable dependencies?
You bet we can!
cmd-exists-%:
@hash $(*) > /dev/null 2>&1 || \
(echo "ERROR: '$(*)' must be installed and available on your PATH."; exit 1)
guard-%:
@if [ -z '${${*}}' ]; then echo 'ERROR: environment variable $* not set' && exit 1; fi
psql-%: cmd-exists-heroku cmd-exists-psql
psql `heroku config:get DATABASE_URL --app=$($(*)_app)`
psql-app: guard-APP cmd-exists-heroku cmd-exists-psql
psql `heroku config:get DATABASE_URL --app=${APP}`
Now if you're missing heroku
or psql
, you get a nice message:
$ make psql-staging
ERROR: 'heroku' must be installed and available on your PATH.
Who knew Make could be so fun to use?
Not done with Make yet
We'll have one more blog post involving Make next week, along with a dump of a bunch of useful Makefile helpers. Stay tuned!
Find this post useful, or interested in learning how we can help you make your next project? Please get in touch!.