Skip to main content

Using the just Task Runner

·11 mins

The just tool is a task runner. It provides a consistent framework for working with sets of tasks, which may be written in any scripting language and can run on multiple platforms.

How just Works #

Each copy of just is a single executable file, with versions for Linux, macOS and Windows. This executable is relatively small, about 6Mb for the 64-bit Linux version. It uses sets of tasks that are defined in plain-text files. You may write a task in any programming language that runs with an interpreter, such as UNIX shells, PowerShell, Python, JavaScript or Nu shell.

This means that you can add just to any environment and use whichever scripting languages are available. If you define multiple implementations of a task, just runs the correct implementation for the current platform. It also provides other features for you to customise the behavior of tasks for different environments.

For example, you may use built-in functions for just in your tasks. These functions include identifying the host environment, reading environment variables, generating UUIDs, calculating file checksums and formatting string inputs. These enable you to get consistent inputs for your tasks across different platforms, even if the scripting language that you use does not have these features.

Terms: In just, tasks are referred to as recipes. The text files that contain recipes are known as justfiles.

You do not need to set up or configure just, because it only requires a copy of the executable, and has no configuration files apart from the files that contain the recipes. Here is an example of a justfile:

# Recipes for Hugo
#
# https://gohugo.io/

# Build Website
build:
    @hugo

# Remove generated files
clean:
    #!/usr/bin/env sh
    set -eu
    HUGO_TEMP_PATHS=".hugo_build.lock public"
    echo "Removing temporary files for Hugo..."
    for HUGO_TEMP_PATH in $HUGO_TEMP_PATHS
    do
      rm -fr $HUGO_TEMP_PATH
      echo "Removed $HUGO_TEMP_PATH"
    done

# Deploy Website
deploy: build
    @hugo deploy

# Run Website in development server
serve:
    @hugo server

The behaviour of just is covered by a backwards compatibility guarantee. To verify that new versions of just do not break compatibility, the just project maintain automation to test against justfiles that are published as Open Source.

Installing just #

Consider using a tool version manager like mise, asdf or a Dev Container feature to download and install just. These can install any version of just that is required, including the latest, because they download executables from GitHub.

For example, this command installs the latest version of just with mise:

mise use -gy just

If you do not wish to use a tool version manager, see the section below for how to install just with a script.

If possible, avoid using operating system packages. These are likely to provide older versions of just.

Adding just to a Dev Container #

If you are using a Dev Container, you can add the feature guiyomh/features/just to your devcontainer.json file to download just from GitHub:

    "features": {
        "ghcr.io/guiyomh/features/just:0": {
            "version": "1.25.2"
        }
    }

Installing just with a Script #

The just project provide a script for downloading just from GitHub. You may either fetch this installation script each time, as the documentation describes, or save it. To ensure that container image builds are consistent, use a saved copy of the script when you build Docker container images.

To save the installation script:

curl -L https://just.systems/install.sh > scripts/install-just.sh

To use the installation script, call it with –tag and –to The –tag specifies the version of just. The –to specifies which directory to install it to:

./scripts/install-just.sh --tag 1.25.2 --to $HOME/.local/bin

Installing just with Operating System Packages #

If you do need to install just with an operating system package manager, it is available for many popular systems. For example, these commands install just:

winget install --id Casey.Just --exact  # winget on Microsoft Windows
brew install just                       # Homebrew on macOS
sudo dnf install just                   # dnf on Fedora Linux

Debian includes just in the testing distribution. Ubuntu will provide just for 24.04 LTS.

See the package list page for what is available from operating system package managers.

Adding a Private Copy of just to a Project #

The instructions that are provided in the previous sections install a copy of just for a user or a system. To install a copy of just that is private to a project, you have several options.

Rust and Node.js projects may use packages for just. To add just to a Node.js project, use the just-install npm package. To include just in a Rust project, add just as a package in Cargo.

Finally, you can use the installation script to install a copy of just into a directory within the project. If you do this, remember to exclude the path of the just executable file from version control.

Enabling Autocompletion #

To enable autocompletion in a shell, use –completions to generate a completion script that you install into the correct location. For example, to enable autocompletion for the Bash shell, run this command:

sudo su -c 'just --completions bash > /etc/bash_completion.d/just.bash'

To install autocompletion for the fish shell, use this command:

just --completions fish > ~/.config/fish/completions

Current versions of just provide autocompletion for Bash, zsh, fish, PowerShell, elvish and Nu.

macOS and Homebrew: If you install just on macOS with Homebrew, follow these instructions to autocompletion for zsh.

Creating a User justfile for Global Tasks #

To define tasks that are available at any time, create a file with the name .user.justfile in your home directory.

Create the first recipe in the root justfile with the name help. Write @just –list in the body of the recipe. When just is invoked without the name of a recipe, it runs the first recipe in the justfile.

Here is an example of a user justfile:

# List available recipes
help:
    @just --list -f "{{ home_directory() }}/.user.justfile"

# Display system information
system-info:
    @echo "CPU architecture: {{ arch() }}"
    @echo "Operating system type: {{ os_family() }}"
    @echo "Operating system: {{ os() }}"
    @echo "Home directory: {{ home_directory() }}"

This justfile requires extra options to run. For convenience, add an alias to your shell configuration. For example, add these lines in .config/fish/config.fish to enable an alias in the Fish shell:

# Add abbr to call recipes in user Justfile by typing ".j RECIPE-NAME"
if command -s just > /dev/null
    abbr --add .j just --justfile $HOME/.user.justfile --working-directory .
end

This means that you run a task in the justfile by entering .j followed by the name of the recipe:

.j system-info

To list the recipes in your user justfile, type .j and press the Enter key.

.j

Using just in a Project #

Use just –init to create a justfile in the root directory of your project. You should always name the just file in the root directory of the project justfile.

If a project only requires one small set of recipes, then use a single justfile. If you need to manage several sets of recipes, use multiple files.

You have two ways to organize the other justfiles in a project:

  1. Modules
  2. Directory hierarchy

You can combine these approaches, but few projects will be complex enough to need to do this.

If you are starting a new project, consider using just modules. Real-world projects often have multiple components with many tasks, and just modules enable you to define clear namespaces for recipes. Modules also provide more flexibility for organizing the files that contain your recipes.

The modules feature is available in just 1.19.0 and above, but it is currently unstable, which means that it is expected to work correctly, but it is not subject to the standard compatibility guarantees of just. This also means that you either need to set the environment variable JUST_UNSTABLE as true, or use the –unstable option when you run commands with just.

Using Modules #

If you decide to use just modules in your project, consider following these guidelines:

  • Create the first recipe in the root justfile with the name help. Write @just –list in the body of the recipe. When just is invoked without a module or recipe name, it runs the first recipe in the justfile.
  • Create an extra mod.just file in each subdirectory that relates to a specific component or type of work. You may not need a separate module for every main subdirectory in the project.
  • Create an extra .just file in the root directory for each tool that applies to the entire project, such as pre-commit.
  • Use the root justfile to define standard tasks for the project. Each of these should call the relevant recipes in one or more modules. Avoid writing recipes in the justfile that do anything other than running recipes that are defined in modules.
  • Remember that the first recipe in each mod.just file is the default for the module. This means that the first recipe runs when a user types the module without specifying the name of the task.
  • Specify the no-cd attribute on each recipe in a module, so that the working directory of the recipe is the root directory of the project.

Example justfile for a Project #

mod precommit  # Defined by pre-commit.just file in root directory
mod python  # Defined by mod.just file in python/ directory

# List available recipes
help:
    @just --unstable --list

# Install tools and dependencies, then set up environment for development
bootstrap:
    @just --unstable install
    @just --unstable setup

# Build artifacts
build:
    @just --unstable python::build

# Install project tools and dependencies
install:
    @just --unstable python::install

# Run all checks
lint:
    @just --unstable pre-commit::check

# Set up environment for development
setup:
    @just --unstable python::setup
    @just --unstable pre-commit::setup

Note that the first recipe in this file is help, so this command runs that recipe:

just

Example just Module for a Project #

# Check the project with pre-commit
check:
    @pre-commit run --all-files

# Run a specific pre-commit check on the project
run hook-id:
    @pre-commit run "{{ hook-id }}" --all-files

# Setup pre-commit for use
setup:
    @pre-commit install

Note that the first recipe in this file is check, so this command runs that recipe:

just pre-commit

Using a Hierarchy of justfiles #

If you decide not to use modules, consider following these guidelines:

  • Create the first recipe in the root justfile with the name help. Write @just –list in the body of the recipe. When just is invoked without the name of a recipe, it runs the first recipe in the justfile.
  • Create an extra justfile in each subdirectory that should be a separate scope of operations. For example, if you have a monorepo, create a child justfile in the main directory for each component.
  • Set fallback to true in each justfile that is NOT in the root directory of the project. This enables just to find recipes from the root justfile as well as the justfile in the current working directory.
  • If you have many recipes for a single justfile, consider putting the recipes into several .just files and using imports to combine them.
  • To ensure that you do not accidentally run a recipe from a user justfile, do NOT set fallback to true in a justfile in the root directory of a project.
  • To create namespaces for recipes, decide a standard prefix for each group of recipes, and set the name of each recipe to start with that prefix, e.g. sys-.
  • Use the no-cd attribute to define recipes that may be executed in one of several different possible directories. By default just sets the working directory to be the location of the justfile that contains the recipe.

Writing justfiles #

Formatting justfiles #

Follow these guidelines when writing justfiles and mod.just modules:

  • Use 4 spaces for indentation. The built-in formatting command sets indentation as 4 spaces.
  • Always put a comment in the line above each recipe. These comments appear next to the recipe in just –list.
  • Use –fmt to format your justfiles. To use this option, run this command in the same directory as the justfile that you want to format:
just --unstable --fmt

–fmt is Currently Unstable: The –fmt subcommand is unstable, which means that it is expected to work correctly, but it is not subject to the standard compatibility guarantees of just.

Writing Recipes #

Follow these guidelines when writing recipes:

  • Use parameters to get inputs for a recipe from the command-line.
  • Use dotenv files to get configuration from files.
  • Remember to use POSIX shell (sh) syntax for single-line recipes. By default, just uses the sh shell on the system.
  • When it is possible, use the built-in functions instead of shell commands, because these will behave consistently across different environments.
  • Use shebang recipes for multi-line shell recipes, as well as recipes in other languages.
  • If you need the features of a specific UNIX shell, use a shebang recipe. Set error handling for recipes that use bash.
  • Use quotes around arguments to ensure that just can identify mistakes.

More Examples of justfiles #

The GitHub project for just includes example justfiles.

Running just Recipes #

To run a recipe in a justfile, enter just followed by the name of the recipe:

just example-recipe

If a recipe accepts parameters, add the value for the parameter to the command:

just example-recipe my-parameter-value

You may always override a variable by specifying a value with the just command:

just example-recipe my-var=my-var-value

Checking justfiles #

To validate a justfile, run –fmt with –check. This returns an exit code of 0 if the justfile is formatted correctly. If the justfile is not correctly formatted, it returns an exit code of 1 and prints a diff.

just --unstable --fmt --check

–fmt is Currently Unstable: The –fmt subcommand is unstable, which means that it is expected to work correctly, but it is not subject to the standard compatibility guarantees of just.

You may also use these two options to check the behavior of just:

  • -n, –dry-run - Prints what just would do without doing it
  • –evaluate - Evaluates and prints all of the variables. If a variable name is given as an argument, it only prints the value of that variable.

Resources #