Skip to content

Instantly share code, notes, and snippets.

@thimslugga
Forked from raspberrypisig/justfile
Last active May 8, 2025 13:07
Show Gist options
  • Save thimslugga/5a2150232a3499509ca1d2417854382b to your computer and use it in GitHub Desktop.
Save thimslugga/5a2150232a3499509ca1d2417854382b to your computer and use it in GitHub Desktop.
A justfile template
#!/usr/bin/env just --justfile
# -*- mode: justfile -*-
# https://just.systems/man/en/introduction.html
# ==============================================================================
# Settings
# ==============================================================================
# Enable unstable features
set unstable := false
# Set the shell used to execute recipes. Array form is recommended.
# -u: Treat unset variables as an error.
# -c: Read commands from string.
set shell := ["bash", "-uc"]
#set shell := ["zsh", "-cu"]
# Set command used to invoke recipes with empty [script] attribute.
#set script-interpreter := ['sh', '-eu']
# Allow recipes appearing later to override earlier recipes with the same name.
set allow-duplicate-recipes := false
# Allow variables appearing later to override earlier variables with the same name.
set allow-duplicate-variables := false
# Allow positional arguments ($1, $2...) in recipes (legacy style).
# Generally prefer named arguments or variadic arguments.
set positional-arguments := true
# Export variables defined in the Justfile to the environment of recipes.
set export := true
# Load variables from a .env file in the Justfile's directory.
# Create a file named `.env` with VAR=VALUE pairs.
set dotenv-load := true
# Set the working directory for recipes *relative to the Justfile directory*.
#set working-directory := "build"
# ==============================================================================
# Variables and Constants
# ==============================================================================
# Simple assignment (evaluated once at parse time)
SIMPLE_VAR := "This is a simple variable"
VERSION := "1.0.0"
# Evaluated assignment (shell command executed at parse time)
CURRENT_DIR = `pwd`
GIT_BRANCH = `git rev-parse --abbrev-ref HEAD`
# Exported variable (available in recipe environments because `set export` is true)
EXPORTED_VAR := "This variable is exported to the shell environment"
# Environment variable override: Just will use the environment's value if set,
# otherwise it uses this default.
# Try running: `MY_NAME="From Env" just show-vars`
MY_NAME := "Default User"
# Variables available for override on the command line:
# `just show-vars override_me="New Value"`
override_me := "Initial Value"
# Conditional assignment using built-in functions
# Available functions: arch(), os(), os_family(), env_var(), env_var_or_default(),
# invocation_directory(), justfile_directory(), num_cpus(), just_executable()
TARGET_OS := if os() == "linux" { "Linux" } else if os() == "macos" { "macOS" } else if os() == "windows" { "Windows" } else { "Unknown OS" }
BUILD_DIR := if os_family() == "windows" { "build\\win" } else { "build/unix" }
# String interpolation
GREETING := "Hello, {{MY_NAME}}!" # Uses the potentially overridden value of MY_NAME
# ==============================================================================
# Aliases
# ==============================================================================
#alias b := build
#alias t := test
#alias d := deploy
#alias c := clean
# ==============================================================================
# Recipes
# ==============================================================================
# Default recipe (runs if `just` is called without arguments)
# Depends on the 'hello' recipe.
default: hello build
@_list:
just --justfile {{ justfile() }} --list --unsorted
echo ""
echo "Available variables:"
just --evaluate | sed 's/^/ /'
echo ""
echo "Override variables using 'just key=value ...' (also ALL_UPPERCASE ones)"
[doc('Evaluate and return all just variables')]
evaluate:
@just --evaluate
# Simple recipe with no dependencies or arguments
hello:
# '@' prefix suppresses echoing the command itself
@echo "===================================="
@echo "{{GREETING}}" # Interpolates the GREETING variable
@echo "Running just version: {{just_executable()}}"
@echo "===================================="
[doc('Return system information')]
system-info:
@echo "os: {{ os() }}"
@echo "family: {{ os_family() }}"
@echo "architecture: {{ arch() }}"
@echo "home directory: {{ home_directory() }}"
# Recipe with dependencies. 'build' runs before 'test'.
# Dependencies are always run, even if the target is up-to-date.
build: _private_setup
@echo "Building project version {{VERSION}} for {{TARGET_OS}} in {{BUILD_DIR}}..."
mkdir -p "{{BUILD_DIR}}"
touch "{{BUILD_DIR}}/artifact.built"
test: build
@echo "Testing the build artifact..."
@echo "Checking existence: {{BUILD_DIR}}/artifact.built"
test -f "{{BUILD_DIR}}/artifact.built"
@echo "Tests passed!"
deploy: test
@echo "Deploying version {{VERSION}}..."
# Simulate deployment
clean: _private_cleanup
@echo "Cleaning up build artifacts..."
rm -rf "{{BUILD_DIR}}"
rm -f artifact.tar.gz
@echo "Clean complete."
# Recipe with required arguments
greet NAME:
@echo "Greetings, {{NAME}}!"
# Recipe with arguments with default values
configure ENVIRONMENT="development":
@echo "Configuring for environment: {{ENVIRONMENT}}"
@# Use `just configure` or `just configure production`
# Recipe with variadic arguments (collects all extra arguments)
# Use '+' prefix. The variable 'ARGS' will be a space-separated string.
process +ARGS:
@echo "Processing arguments:"
@# Using shell loop to iterate through space-separated args
@for arg in {{ARGS}}; do \
echo " - Got arg: $$arg"; \
done
# Recipe demonstrating positional arguments (requires `set positional-arguments`)
# Usage: `just positional first second third`
positional $arg1 $arg2="default_val" $arg3="another_default":
@echo "Positional Args:"
@echo " Arg 1: {{arg1}}"
@echo " Arg 2: {{arg2}}"
@echo " Arg 3: {{arg3}}"
# Recipe demonstrating capturing all positional args into one var (requires `set positional-arguments`)
# Usage: `just positional_all one two three`
positional_all *args:
@echo "All positional args captured: {{args}}"
# Recipe using a different interpreter via shebang
# The script content follows the shebang line.
python_script: deptask
#!/usr/bin/env python
import os
import platform
print(f"Hello from Python inside Just!")
print(f"OS: {os.name}, Platform: {platform.system()}, Arch: {platform.machine()}")
# Accessing exported env var
exported = os.getenv("EXPORTED_VAR", "Not Found")
print(f"Accessed exported variable: {exported}")
# Another shebang example using Bash explicitly
bash_script:
#!/bin/bash
echo "Hello from an explicit Bash script!"
echo "My shell is $SHELL"
echo "Current directory is $(pwd)"
# Recipe demonstrating backtick command evaluation within the recipe body
show_date:
@echo "The current date is: `date +'%Y-%m-%d %H:%M:%S'`"
@echo "Current git branch evaluated in recipe: `git rev-parse --abbrev-ref HEAD`"
# Recipe demonstrating built-in functions
show_info:
@echo "== System Info =="
@echo "OS Type: {{os()}}"
@echo "OS Family: {{os_family()}}"
@echo "Architecture: {{arch()}}"
@echo "Number of CPUs: {{num_cpus()}}"
@echo "== Just Info =="
@echo "Justfile Dir: {{justfile_directory()}}"
@echo "Invocation Dir: {{invocation_directory()}}"
@echo "== Env Vars =="
@echo "PATH (env_var): {{env_var('PATH')}}"
@echo "SHELL (or default): {{env_var_or_default('SHELL', '/bin/sh')}}"
@echo "MY_NAME: {{MY_NAME}}" # Shows default or env override
@echo "Override Me: {{override_me}}" # Shows default or command-line override
# Accessing dotenv var (requires .env file and `set dotenv-load`)
@echo "Dotenv Var: {{env_var_or_default('MY_DOTENV_VAR', 'Not Set')}}"
# Recipe showing quoting and interpolation edge cases
quoting:
# Simple quotes are preserved
@echo 'Single quotes work'
@echo "Double quotes work"
# Interpolation works in doubles, not singles
@echo "Simple Var: {{SIMPLE_VAR}}"
@echo 'Simple Var: {{SIMPLE_VAR}}' # This won't interpolate
# Escaping characters
@echo "This recipe shows \"quotes\" and backslashes \\ and dollar signs \$"
# Complex interpolation
COMPLEX_STRING := "Value with 'quotes' and spaces"
@echo "Complex variable: {{COMPLEX_STRING}}"
# Using expressions in interpolation (less common, usually done in vars)
@echo "OS check inline: {{ if os() == 'linux' {'yay linux!'} else {'not linux'} }}"
# Recipe demonstrating error handling
# '-' prefix: Ignore errors from this command. The recipe continues.
# '!' prefix: Run this command even if `just` was invoked with --dry-run.
error_handling:
@echo "Attempting to remove a non-existent file (will likely fail)..."
-rm no_such_file_here.txt
@echo "Recipe continued after ignored error."
@echo "Now creating a file (forced run even in dry-run)..."
!touch i_was_created_anyway.tmp
@echo "Now attempting a command that should succeed..."
ls .
@echo "Finished error handling demo."
-rm i_was_created_anyway.tmp # Clean up forced file
# ==============================================================================
# Recipe Attributes - Modify recipe behavior conditionally
# ==============================================================================
# [private]: Not shown in `just --list` or `just -l`. Can still be run directly.
[private]
_private_setup:
@echo "(Private) Setting up..."
touch private_setup.flag
# Recipes starting with '_' are also private by default.
_private_cleanup:
@echo "(Private) Cleaning up..."
rm -f private_setup.flag
# [no-cd]: Run the recipe in the directory where `just` was invoked,
# not the Justfile's directory (or `working-directory` if set).
[no-cd]
run_in_invocation_dir:
@echo "This recipe runs in: `pwd` (should be where you ran 'just')"
@echo "The Justfile is in: {{justfile_directory()}}"
# [confirm]: Prompt the user before running the recipe.
[confirm]
dangerous_action:
@echo "WARNING: Performing a potentially dangerous action!"
# rm -rf / # Don't actually do this! :)
@echo "Action performed (simulation)."
# OS-specific recipes
[linux]
linux_only:
@echo "This runs only on Linux. uname: `uname -s`"
[macos]
macos_only:
@echo "This runs only on macOS. uname: `uname -s`"
[windows]
windows_only:
@echo "This runs only on Windows. Systeminfo snippet:"
@systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
# [unix] is a shorthand for [linux] or [macos]
[unix]
unix_only:
@echo "This runs on Linux or macOS. Family: {{os_family()}}"
# ==============================================================================
# Imports and Modules - Include other Justfiles
# ==============================================================================
# Requires a file named `module.just` in the same directory.
# Example `module.just`:
# ```justfile
# # File: module.just
# export module_var := "Variable from module"
#
# # Recipe exported from module
# module_recipe NAME="Module User":
# echo "Hello {{NAME}} from the module recipe!"
# echo "Module variable access: {{module_var}}"
#
# _private_module_recipe:
# echo "This is private to the module"
# ```
import "module.just" # Imports recipes and exported variables
# Use imported recipe and variable
use_module: module_recipe "Just User" # Pass argument to module recipe
@echo "Accessing imported variable in main file: {{module_var}}"
# Cannot call _private_module_recipe from here
# ==============================================================================
# Miscellaneous / Edge Cases
# ==============================================================================
# Task that depends on an imported task
depends_on_module: module_recipe
@echo "Main task ran after module recipe."
# A task just for demonstrating dependencies
deptask:
@echo "Running dependency task..."
# Recipe with line continuations using backslash '\'
long_command:
@echo "This is the first part of a long command..." \
&& echo "This is the second part, connected by &&" \
&& echo "And this is the third part."
# Empty recipe (useful as a dependency placeholder)
placeholder:
# Recipe showing how exported variables work with subshells
check_export: export_check_subshell
@echo "Checking EXPORTED_VAR in main recipe: $EXPORTED_VAR" # Access via shell
export_check_subshell:
#!/bin/bash
echo "Checking EXPORTED_VAR in subshell script:"
if [ -n "$EXPORTED_VAR" ]; then
echo " EXPORTED_VAR is set: '$EXPORTED_VAR'"
else
echo " EXPORTED_VAR is NOT set."
fi
echo "Checking SIMPLE_VAR in subshell script (should not be exported):"
if [ -n "$SIMPLE_VAR" ]; then
echo " SIMPLE_VAR is set: '$SIMPLE_VAR'"
else
echo " SIMPLE_VAR is NOT set."
fi
# Final recipe to show variable evaluation timing
show_vars:
@echo "== Variable Values During Recipe Execution =="
@echo "Simple Var: {{SIMPLE_VAR}}"
@echo "Evaluated Dir: {{CURRENT_DIR}}" # Value captured at parse time
@echo "Git Branch: {{GIT_BRANCH}}" # Value captured at parse time
@echo "Target OS: {{TARGET_OS}}"
@echo "Build Dir: {{BUILD_DIR}}"
@echo "MY_NAME: {{MY_NAME}}" # Shows resolved value (env > justfile default)
@echo "Override Me: {{override_me}}" # Shows resolved value (cmdline > justfile default)
@echo "Dotenv Var: {{env_var_or_default('MY_DOTENV_VAR', 'Not Set')}}"
@echo "Current Dir (recipe eval): `pwd`" # Value evaluated now
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment