# ©AngelaMos | 2026 # justfile.example # # Annotated justfile template for Cybersecurity-Projects. # Copy into your project root, rename to `justfile`, and replace # every with your actual values. Delete sections # that don't apply to your project's stack. # # Docs: https://just.systems/man/en/ # Cheat: https://cheatography.com/linux-china/cheat-sheets/justfile/ # # Install just: # cargo install just (from source) # brew install just (macOS) # apt install just (Debian/Ubuntu 24.04+) # pipx install rust-just (via pipx) # # Usage: # just list all available recipes # just run a recipe # just -n dry-run (print commands without executing) # ── Settings ────────────────────────────────────────────────────────────────── # These apply globally to every recipe in the file. # Load .env file into environment for all recipes. set dotenv-load # Export all `just` variables as environment variables to child processes. set export # Use bash in strict mode (-u = error on unset vars, -c = command string). # This gives consistent behavior across macOS/Linux regardless of login shell. set shell := ["bash", "-uc"] # Windows equivalent (only takes effect on Windows, ignored elsewhere). set windows-shell := ["powershell.exe", "-NoLogo", "-Command"] # ── Variables ───────────────────────────────────────────────────────────────── # Variables defined here are available in all recipes via {{name}}. # Backtick expressions run shell commands at parse time. project := file_name(justfile_directory()) version := `git describe --tags --always 2>/dev/null || echo "dev"` # Project-specific paths — change these to match your layout. # Python-only project: src_dir := "src" # Fullstack project: src_dir := "backend/src" src_dir := "" test_dir := "" package := "" # ── Default ─────────────────────────────────────────────────────────────────── # The first recipe is the default (runs when you type `just` with no args). # --unsorted preserves the order defined in this file instead of alphabetizing. default: @just --list --unsorted # ============================================================================= # Linting and Formatting # ============================================================================= # Ruff handles both linting and formatting for Python. # `*ARGS` is a variadic parameter — any extra flags you pass get forwarded. # Example: just ruff --fix [group('lint')] ruff *ARGS: ruff check {{src_dir}}/ {{test_dir}}/ {{ARGS}} [group('lint')] ruff-fix: ruff check {{src_dir}}/ {{test_dir}}/ --fix ruff format {{src_dir}}/ {{test_dir}}/ [group('lint')] ruff-format: ruff format {{src_dir}}/ {{test_dir}}/ [group('lint')] pylint *ARGS: pylint {{src_dir}}/{{package}} {{ARGS}} # Dependency recipes — `lint` runs `ruff` then `pylint` in order. [group('lint')] lint: ruff pylint # ============================================================================= # Frontend Linting (delete this section for Python-only projects) # ============================================================================= # Biome is an all-in-one linter/formatter for JS/TS (replaces ESLint+Prettier). # Alternatives: eslint + prettier (shown commented below) [group('frontend')] biome *ARGS: cd frontend && pnpm biome check . {{ARGS}} [group('frontend')] biome-fix: cd frontend && pnpm biome check --write . [group('frontend')] stylelint *ARGS: cd frontend && pnpm stylelint '**/*.scss' {{ARGS}} [group('frontend')] stylelint-fix: cd frontend && pnpm stylelint '**/*.scss' --fix [group('frontend')] tsc *ARGS: cd frontend && pnpm tsc --noEmit {{ARGS}} # Alternative: ESLint + Prettier (uncomment if not using Biome) # [group('frontend')] # eslint *ARGS: # cd frontend && pnpm eslint . {{ARGS}} # # [group('frontend')] # eslint-fix: # cd frontend && pnpm eslint . --fix # # [group('frontend')] # prettier *ARGS: # cd frontend && pnpm prettier --check "src/**/*.{ts,tsx,css}" {{ARGS}} # # [group('frontend')] # prettier-fix: # cd frontend && pnpm prettier --write "src/**/*.{ts,tsx,css}" # ============================================================================= # Type Checking # ============================================================================= [group('types')] mypy *ARGS: mypy {{src_dir}}/ {{ARGS}} # ty is Astral's experimental type checker (fast, Rust-based). # Uncomment if your project uses it. # [group('types')] # ty *ARGS: # ty check {{ARGS}} [group('types')] typecheck: mypy # ============================================================================= # Testing # ============================================================================= [group('test')] pytest *ARGS: pytest {{test_dir}}/ {{ARGS}} [group('test')] test: pytest [group('test')] test-cov: pytest {{test_dir}}/ --cov={{src_dir}}/{{package}} --cov-report=term-missing --cov-report=html # ============================================================================= # CI / Quality # ============================================================================= # Composite recipes that chain multiple checks. # `ci` is the full pipeline; `check` is the fast subset (no tests). [group('ci')] ci: lint typecheck test [group('ci')] check: ruff mypy # ============================================================================= # Docker Compose — Production (delete if no Docker) # ============================================================================= # These wrap `docker compose` (compose.yml). # Default params use `service='backend'` syntax — overridable at call site. [group('docker')] up *ARGS: docker compose up {{ARGS}} [group('docker')] start *ARGS: docker compose up -d {{ARGS}} [group('docker')] down *ARGS: docker compose down {{ARGS}} [group('docker')] stop: docker compose stop [group('docker')] build *ARGS: docker compose build {{ARGS}} [group('docker')] rebuild: docker compose build --no-cache [group('docker')] logs *SERVICE: docker compose logs -f {{SERVICE}} [group('docker')] ps: docker compose ps [group('docker')] shell service='backend': docker compose exec -it {{service}} /bin/bash # ============================================================================= # Docker Compose — Dev (delete if no Docker) # ============================================================================= # Same commands but targeting dev.compose.yml. [group('dev')] dev-up *ARGS: docker compose -f dev.compose.yml up {{ARGS}} [group('dev')] dev-start *ARGS: docker compose -f dev.compose.yml up -d {{ARGS}} [group('dev')] dev-down *ARGS: docker compose -f dev.compose.yml down {{ARGS}} [group('dev')] dev-stop: docker compose -f dev.compose.yml stop [group('dev')] dev-build *ARGS: docker compose -f dev.compose.yml build {{ARGS}} [group('dev')] dev-rebuild: docker compose -f dev.compose.yml build --no-cache [group('dev')] dev-logs *SERVICE: docker compose -f dev.compose.yml logs -f {{SERVICE}} [group('dev')] dev-ps: docker compose -f dev.compose.yml ps [group('dev')] dev-shell service='backend': docker compose -f dev.compose.yml exec -it {{service}} /bin/bash # ============================================================================= # Database — Docker (delete if no database) # ============================================================================= # Alembic migrations executed inside the running backend container. [group('db')] migrate *ARGS: docker compose exec backend alembic upgrade {{ARGS}} [group('db')] migration message: docker compose exec backend alembic revision --autogenerate -m "{{message}}" [group('db')] rollback: docker compose exec backend alembic downgrade -1 [group('db')] db-history: docker compose exec backend alembic history --verbose [group('db')] db-current: docker compose exec backend alembic current # ============================================================================= # Database — Local (no Docker) (delete if no database) # ============================================================================= [group('db-local')] migrate-local *ARGS: cd backend && uv run alembic upgrade {{ARGS}} [group('db-local')] migration-local message: cd backend && uv run alembic revision --autogenerate -m "{{message}}" [group('db-local')] rollback-local: cd backend && uv run alembic downgrade -1 [group('db-local')] db-history-local: cd backend && uv run alembic history --verbose [group('db-local')] db-current-local: cd backend && uv run alembic current # ============================================================================= # Setup # ============================================================================= # First-time project bootstrap. For fullstack projects, chain sub-recipes. [group('setup')] setup: @echo "Setting up development environment..." uv sync --all-extras @echo "Setup complete!" # Fullstack alternative (uncomment and replace the simple setup above): # [group('setup')] # setup: setup-backend setup-frontend env # @echo "Setup complete!" # # [group('setup')] # setup-backend: # @echo "Setting up backend..." # cd backend && uv sync # @echo "Backend ready!" # # [group('setup')] # setup-frontend: # @echo "Setting up frontend..." # cd frontend && pnpm install # @echo "Frontend ready!" # # [group('setup')] # env: # @if [ ! -f .env ]; then cp .env.example .env; echo "Created .env from .env.example"; fi # @echo ".env ready — update with your values." # ============================================================================= # Utilities # ============================================================================= [group('util')] info: @echo "Project: {{project}}" @echo "Version: {{version}}" @echo "OS: {{os()}} ({{arch()}})" # The `-` prefix ignores errors (e.g., dir doesn't exist). Keeps going. [group('util')] clean: -rm -rf .mypy_cache -rm -rf .pytest_cache -rm -rf .ruff_cache -rm -rf htmlcov -rm -f .coverage -rm -rf dist -rm -rf build @echo "Cache directories cleaned" # [confirm(...)] prompts for Y/n before running — safety net for destructive ops. [group('util')] [confirm("Remove all build artifacts, caches, and virtual environment?")] nuke: @echo "Nuking everything..." -rm -rf .mypy_cache .pytest_cache .ruff_cache htmlcov dist build -rm -f .coverage -rm -rf .venv @echo "Nuke complete!"