set shell := ["bash", "-c"] export UV_FROZEN := "true" # List available commands [private] @default: just --list echo "" echo "For help with a specific recipe, run: just --usage " # ============================================================================ # Run # ============================================================================ # Run the application in production mode [group("run")] run: uv run uvicorn grimoire.app:create_app --factory --loop asyncio --port 8000 # Run the Docker container [group("run")] docker-run: docker run -p 8000:8000 \ -v ./config.yaml:/app/config.yaml:ro \ -v ./data:/app/data:ro \ grimoire # ============================================================================ # Development # ============================================================================ # Run the application in development mode (with auto-reload) [group("dev")] dev: uv run uvicorn grimoire.app:create_app --factory --reload --reload-dir src --reload-dir data --reload-include "*.yaml" --loop asyncio --port 8000 # Run all quality checks [group("dev")] check: format lint test @echo; echo "✓ All checks passed!" # Format the codebase [group("dev")] format: # Fix generic linting issues uv run ruff check --fix-only # Fix import-related issues (including ordering) uv run ruff check --select=I --fix-only # Format the code uv run ruff format # Lint the codebase [group("dev")] lint: # Lint the code uv run ruff check # Run static checks uv run pyright src # Check for misspellings uv run codespell src # Check for dead code uv run vulture src # Run actionlint on GitHub Actions workflows test -d .github/workflows && uv run actionlint || true @echo "✓ Linting passed!" # Run tests [group("dev")] test: uv run pytest # Run tests with coverage information [group("dev")] coverage: uv run pytest --cov=src --cov-report=term-missing # Sync and activate the virtual environment (.venv) [group("dev")] venv: @echo "Activating virtual environment..." @uv sync --all-groups --all-extras @. .venv/bin/activate; exec "$SHELL" -i # ============================================================================ # Documentation # ============================================================================ # Serve documentation locally (live reload) [group("docs")] docs: uv run --group docs mkdocs serve --config-file docs/user/mkdocs.yml --dev-addr localhost:8100 # Build documentation site [group("docs")] docs-build: uv run --group docs mkdocs build --config-file docs/user/mkdocs.yml # ============================================================================ # Build & Package # ============================================================================ # Build the project [group("build")] build: uv build # Build the Docker image [group("build")] docker-build: docker build -t grimoire . # Remove build artifacts and temporary files [group("build")] clean: # Remove __pycache__ directories find . -type d -name "__pycache__" -exec rm -r {} + || true # Remove .ruff_cache directory rm -rf .ruff_cache # Remove .pytest_cache directory rm -rf .pytest_cache # Remove build/dist/egg-info directories rm -rf build dist *.egg-info # Remove coverage reports rm -f .coverage coverage.xml # ============================================================================ # Maintenance # ============================================================================ # Update this justfile from the blueprint repository [group("maintenance")] [confirm("Fetch the justfile from *lucabello/blueprints* ? (y/n):")] @refresh: echo "Fetching latest justfile from blueprints repository..." gh api repos/lucabello/blueprints/contents/blueprints/python/justfile --jq '.content' | base64 --decode > justfile echo "✓ justfile updated" # Update uv.lock dependencies [group("maintenance")] lock: unset UV_FROZEN; uv lock --upgrade --no-cache # Scan for security vulnerabilities [group("maintenance")] scan: @echo "Scanning for security vulnerabilities..." uv run uv-secure # ============================================================================ # Version & Release # ============================================================================ # Bump the version in pyproject.toml [group("release")] [arg("level", long="level", pattern="^(major|minor|patch)$", help="Semantic version bump level")] bump level: #!/usr/bin/env bash uv version --bump={{level}} # Publish to PyPI (requires authentication) [group("release")] [arg("test", long="test", help="Publish to TestPyPI")] publish test="false": clean build #!/usr/bin/env bash if [ "{{test}}" = "true" ]; then uv publish --index testpypi else uv publish fi # Publish Docker image to DockerHub (requires authentication) [group("release")] publish-docker: #!/usr/bin/env bash version="$(yq -oy .project.version pyproject.toml)" echo "Building and publishing grimoire:${version} to DockerHub..." docker build -t lucabello/grimoire:"${version}" -t lucabello/grimoire:latest . docker push lucabello/grimoire:"${version}" docker push lucabello/grimoire:latest echo "✓ Published lucabello/grimoire:${version} and lucabello/grimoire:latest" # Create a GitHub release and publish to PyPI and DockerHub [group("release")] release: (publish "false") publish-docker #!/usr/bin/env bash latest_release="$(gh release list --limit=1 --order=desc --json=tagName | jq -r '.[].tagName')" echo "Latest release on GitHub is ${latest_release}" pyproject_release="$(yq -oy .project.version pyproject.toml)" echo "Current version in pyproject.toml is v${pyproject_release}" if [[ "${latest_release}" == "v${pyproject_release}" ]] ; then echo "Error: Latest release version matches pyproject.toml version. Bump the version first by using 'just bump'." exit 1 fi read -p "Proceed releasing v${pyproject_release}? (y/n): " answer if [[ ! "$answer" == [Yy] ]] ; then echo "Cancelled." exit 1 fi gh release create "v${pyproject_release}" --generate-notes --notes-start-tag="${latest_release}"