just Make

This year i’ve really started to use Make and therefore Makefiles. For university, for work, for open-source and simply for me because i’m lazy and i like to type the least amount possible. Especially after reading this TIL from Sheogorath and the referenced blog by Victoria Drake i felt like using self-documenting Makefiles like this:

SHELL := /bin/bash
.POSIX:
.PHONY: help lint install

help: ## Show this help
    @egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

lint: ## Lint ansible files with ansible-lint
    ansible-lint

install: ## Install or update requirements
    ansible-galaxy install -f -r requirements.yml

for anything.

But …

Make inherently is a build tool and not a general purpose command runner.

The latter is what i (mis)used it for (most if not all of my targets are .PHONY). Which doesn’t make for the best experience as some things are not feasible with Make. Make isn’t even designed to do some things i did.

I realized this mainly because i tried to simplify my ansible-playbook commands. The structure of my infrastructure repository leads me to run a playbook command like ansible-playbook playbooks/<playbook>.yml. Admittedly with shell completitions this is not the worst, but somehow i still wanted to be able to run something like make play <playbook> … which isn’t really possible. Yes, of course there are StackOverflow answers on how to make something like this work, but they were obviously more of a workaround than intended usage of make. So i didn’t really want to use those. Luckily i did’nt have to, because burrowed deep down in the comments someone was mentioning ‘Just’. But just what is Just?

make with Just

:robot: Just a command runner to quote the Just repository description. And that is really accurate. It just let’s you save shell commands to a ‘Justfile’ and they can then be run with just <target>. ‘Justfile’ syntax is inspired by Make with some changes for specific features. Also what Make calls ’targets’ Just calls ‘recipes’.

Having found this tool of course i wanted to see if this does make my original plan feasible. Spoiler: yes it does. Just has a feature called Recipe Parameters which lets me do the following:

# Run the specified playbook 'book' from the ./playbooks directory
play book:
    ansible-playbook playbooks/{{book}}.yml

and with this i can just do just play podman 🎉

Taking it a step further, with Just you can even define multiple parameters and the last parameter may be variadic by prefixing it with * or +.

# Run the specified playbook 'book' from the ./playbooks directory with the given '*flags'
play book *flags:
    ansible-playbook {{flags}} playbooks/{{book}}.yml

Now i can use just play podman --limit=<host> 🎊

self-documenting still possible?

Yes! Just even comes with it by default! You can already see in my examples that i put a comment above my recipes. just --list prints all recipes in the Justfile and if there is a comment above displays it as well.

$ just --list
Available recipes:
    play book *flags # Run the specified playbook 'book' from the ./playbooks directory with the given '*flags'

Because if called with no arguments Just uses the first recipe in the file (just like make does) adding a help recipe has the same effect as a self-documenting Makefile

# Show this help
help:
    @just --list

In the end i ended up with the following Justfile

# Show this help
help:
    @just --list

# Lint ansible files with ansible-lint
lint:
    ansible-lint

# Install or update requirements
install:
    ansible-galaxy install -f -r requirements.yml

# Run the specified playbook 'book' from the ./playbooks directory with the given '*flags'
play book *flags:
    -ansible-playbook {{flags}} playbooks/{{book}}.yml

📝 @ supresses printing the command to stderr and - ignores errors.

Conclusion

Just just seems like the tool i always wanted Make to be, so i will have a lot of fun with it. Using Just to make use of multiple ansible role skeletons will most likely be one of my next steps. Because i really don’t want to type ansible-galaxy init --role-skeleton=.ansible/rootless_podman_container role_name.

Useless Information

number of ‘make’s used: 24

number of ‘just’s used: 33