Skip to content

Instantly share code, notes, and snippets.

@raspberrypisig
Created April 19, 2025 06:21
Show Gist options
  • Select an option

  • Save raspberrypisig/bfc02ca302315e6a3437722cfc81d9bc to your computer and use it in GitHub Desktop.

Select an option

Save raspberrypisig/bfc02ca302315e6a3437722cfc81d9bc to your computer and use it in GitHub Desktop.

Revisions

  1. raspberrypisig created this gist Apr 19, 2025.
    341 changes: 341 additions & 0 deletions justfile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,341 @@
    # -*- mode: justfile -*-

    # ==============================================================================
    # Settings - Control Just's behavior
    # ==============================================================================

    # 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"]

    # Export variables defined in the Justfile to the environment of recipes.
    set export

    # Allow positional arguments ($1, $2...) in recipes (legacy style).
    # Generally prefer named arguments or variadic arguments.
    set positional-arguments

    # Load variables from a .env file in the Justfile's directory.
    # Create a file named `.env` with VAR=VALUE pairs.
    # Example .env:
    # MY_DOTENV_VAR="Hello from .env!"
    # ANOTHER_VAR=123
    set dotenv-load

    # Set the working directory for recipes *relative to the Justfile directory*.
    # set working-directory := "build" # Uncomment to activate

    # ==============================================================================
    # 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 - Shorter names for recipes
    # ==============================================================================

    alias b := build
    alias t := test
    alias d := deploy
    alias c := clean

    # ==============================================================================
    # Recipes - The core tasks
    # ==============================================================================

    # Default recipe (runs if `just` is called without arguments)
    # Depends on the 'hello' recipe.
    default: hello build

    # 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 "===================================="

    # 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