Just Modules¶
Recipes are organised into just modules declared in Justfile.tmpl. For the
what-belongs-where split between daily-use recipes and maintainer recipes, see
Dotfiles vs Maint.
Discovery¶
Every module inherits common recipes (list, show, usage, help). The list recipe accepts variadic
*submodules, so it absorbs space-separated module tokens that follow it. Combined with just's left-to-right module
traversal, list can appear at any position:
just list docker compose # list absorbs "docker compose" as submodules
just docker list compose # docker's list absorbs "compose"
just docker compose list # Traverse to docker::compose, run list
just docker::compose::list # Fully qualified namepath
just docker::compose # Module default (delegates to list)
Once just reaches a recipe (like list), remaining tokens are arguments — and :: in arguments is fine:
just list docker::compose # "docker::compose" passed as a single submodule arg
just docker::list compose # docker::list is a recipe, "compose" is an arg
just maint::list test::smoke # maint::list is a recipe, "test::smoke" is an arg
Gotcha: just docker::compose list does not run docker::compose::list. Because docker::compose resolves to the
module's default recipe (which accepts *submodules), the word list is absorbed as a submodule argument — building
the path docker::compose::list, which doesn't exist. Use all spaces or all :::
just docker::compose list # Fails — "list" becomes a submodule arg
just docker compose list # Works — space traversal, then list recipe
just docker::compose::list # Works — fully qualified recipe
Why the default is variadic¶
The default declares *submodules (variadic) so it absorbs every trailing token. If it took a fixed arity instead — say
one submodule arg — excess tokens would chain as separate root-level recipe invocations: just docker::compose foo bar
would consume foo and then attempt bar as a top-level recipe. The variadic prevents that, at the cost of the gotcha
above.
Recipe Naming Conventions¶
Public Recipes¶
| Pattern | Meaning |
|---|---|
debug-* |
Create container, run setup, then drop into a ZSH shell |
*-local |
Apply configuration from your LOCAL source (dev mode) |
*-repo |
Apply configuration from the GIT REMOTE (prod/CI mode) |
Recipe Arguments¶
| Argument | Usage |
|---|---|
distro |
Target OS: debian, fedora, or bluefin (default: debian) |
Example: just maint test smoke debug-local bluefin
Private Helpers¶
| Pattern | Purpose |
|---|---|
_require-<tool> |
Fail-fast guard. Checks that <tool> is installed and exits with a helpful error if not. |
Thin wrappers around the generic _require recipe in _common.just. |
|
_ensure-<tool> |
Self-healing bootstrapper. Installs or configures <tool> if missing, silently succeeds |
if present. May depend on a _require-* guard. |
|
_pick-<resource> |
Interactive fzf selector. Returns the user's selection to stdout. |
Always depends on _require-fzf. |
Style Guide¶
- Group related recipes using
[group('name')] - Document recipes with comments above them
- Use private helpers (prefix with
_) for internal logic - Prefer dependencies over
justsubprocess calls where possible
Module Help Files¶
Each just module can have a help.md file rendered by just <module> help. These are runtime help for the user — not
architecture docs, not implementation details. When writing or editing module help:
- Do not duplicate
just --list— the one-line comment above each recipe is already available viajust <module>. Help files should add context that the one-liner cannot. - Focus on practical runtime guidance — when to reach for a recipe, what to expect, what choices to make.
- Include gotchas and non-obvious behaviour — e.g. "will fail if you haven't pushed", "Bluefin tests are slower", "requires confirmation".
- Do not explain chezmoi/Docker/tool internals — that belongs in the architecture docs.