---
title: "Just"
url: https://perrotta.dev/2024/12/just/
last_updated: 2026-01-03
---


[Just](https://github.com/casey/just) is a command runner, a modern replacement
for GNU Make.

It is written in Rust, has sensible defaults, and lots of syntactic sugar.
A good analogy is `fish` versus `bash` when comparing `just` to `make`.

It's very easy to learn from its
[README.md](https://github.com/casey/just?tab=readme-ov-file) alone as it's
quite comprehensive. There's also a [gitbook](https://just.systems/man/en/).

[Simon Willison](https://til.simonwillison.net/django/just-with-django) prompted
me to try it out.

As an exercise I decided to convert the `Makefile` used to manage this blog into
a `Justfile`.

The original `Makefile`:

```makefile
# Sitemap URL
SITEMAP = https://perrotta.dev/sitemap.xml

# Hugo port
PORT := 1313

# Abort if hugo is not installed.
ifeq (, $(shell which hugo))
  $(error "No hugo in $$PATH, install it first")
endif

all:
	hugo server --bind="0.0.0.0" --buildDrafts --port $(PORT) --watch

build:
	hugo --environment production --gc --minify

clean:
	$(RM) -r public/ resources/

ping:
	# Ping Google about changes in the sitemap
	curl -sS -o /dev/null "https://www.google.com/ping?sitemap=$(SITEMAP)"
	# Ping Bing (DuckDuckGo, etc) about changes in the sitemap
	curl -sS -o /dev/null "https://www.bing.com/ping?sitemap=$(SITEMAP)"

.PHONY: all build clean ping
```

Initially I asked ChatGPT to convert it to a `Justfile` but it was a disaster,
even after a couple of iterations. Then I did it myself[^1]. The `Justfile`:

```justfile
set dotenv-load

watch:
	hugo server --buildDrafts --port ${PORT:-1313} --watch

build:
	hugo --environment production --gc --minify

# Create a new post. Usage: `just new "advent of code day 8"`
new post:
	hugo new content/posts/`date "+%Y-%m-%d"`-{{ kebabcase(post) }}.md

clean:
	rm -rf public/ resources/

# Ping Google and Bing about changes in the sitemap
ping sitemap="https://perrotta.dev/sitemap.xml":
	curl -sS -o /dev/null "https://www.google.com/ping?sitemap={{ sitemap }}"
	curl -sS -o /dev/null "https://www.bing.com/ping?sitemap={{ sitemap }}"
```

The main differences:

- Environment variables: use `{{ foo }}` instead of `$(FOO)`
- Exception: environment variables loaded from `.env` (via `set dotenv-load`)
  use `$FOO` or `${FOO}` instead, like POSIX shell variables
- Use `rm -rf` instead of `$(RM) -r`
- Rules accept parameters. Look at `new post` as an example. Example usage:
  `just new "advent of code day 8"`
- Run shell commands within rules with backticks. `$(cmd)` does not work.
- Some handy out-of-the-box functions such as `kebabcase()`. No need to
  implement this kind of string manipulation in plain shell script!
- Documentation comments above rules are recognized. They are displayed as help
  / usage text when running `just -l`. No need for hacky [self-documented
  `Makefile`]({{< ref "2024-08-08-self-documented-makefiles" >}}) setups!

```shell
% just -l
Available recipes:
    build
    clean
    new post # Create a new post. Usage: `just new "advent of code day 8"`
    ping sitemap="https://perrotta.dev/sitemap.xml" # Ping Google and Bing about changes in the sitemap
    watch
```

[^1]: With a bit of LLM prompting in lieu of Google or Stack Overflow searches.

