Skip to content

Extending Rhiza

This guide provides comprehensive examples and best practices for extending and customizing Rhiza-based projects.

Table of Contents


Overview

Rhiza's modular design allows you to extend functionality without modifying template-managed files. This ensures:

  • Template sync compatibility - Your customizations survive template updates
  • Clean separation - Project-specific code stays separate from framework code
  • Easy maintenance - Changes are localized and well-organized
  • Team flexibility - Developers can have personal local overrides

Where to Add Customizations

Location Purpose Committed to Git?
Root Makefile Project-wide customizations ✅ Yes
local.mk Developer-local overrides ❌ No (gitignored)
.rhiza/.env Environment variables ✅ Yes (optional)

Important: Never modify files in .rhiza/ directly—they are template-managed and will be overwritten during sync operations.


Step-by-Step: Add a New Bundle

Use this checklist when you want to add a new bundle to Rhiza itself.

Worked Example: linter

This example adds a minimal linter bundle that injects a .ruff.toml override into downstream projects.

  1. Create the bundle directory under bundles/

Every bundle must have a matching directory under bundles/<name>/.

bundles/
└── linter/
    └── .ruff.toml
  1. Add the bundle files

Files inside bundles/linter/ are copied into the downstream project at the same relative path. A minimal Ruff override can extend the core bundle's ruff.toml and add stricter rules:

# bundles/linter/.ruff.toml
extend = "ruff.toml"

[lint]
extend-select = ["B", "I"]

In a synced downstream project, that produces:

.ruff.toml
  1. Declare the bundle in .rhiza/template-bundles.yml

Add metadata for the new bundle and declare any dependencies it needs. Because this example extends the core bundle's Ruff config, it should require core.

linter:
  description: "Optional Ruff overrides for stricter linting"
  standalone: false
  requires: [core]
  1. Add bundle content tests in tests/bundles/

Add a focused sync test so the new bundle is exercised the same way downstream projects receive it. For this example, extending tests/bundles/test_bundle_combinations.py is enough:

class TestLinterBundleSync:
    """Syncing core + linter adds the Ruff override file."""

    @pytest.fixture(autouse=True)
    def synced(self, tmp_path: Path, root: Path) -> None:
        sync_bundles(root, ["core", "linter"], tmp_path)
        self.project = tmp_path

    def test_ruff_override_exists(self) -> None:
        assert (self.project / ".ruff.toml").is_file()

    def test_ruff_override_extends_core_config(self) -> None:
        content = (self.project / ".ruff.toml").read_text(encoding="utf-8")
        assert 'extend = "ruff.toml"' in content

Existing tests in tests/bundles/test_bundle_content_validity.py will also pick up any new YAML or JSON files automatically.

  1. Document the bundle in CLAUDE.md and README.md

Mention the new bundle in CLAUDE.md's bundle overview (under "Template Bundles") and add a row to the matching bundle table in README.md ("Feature bundles", "Platform overlay bundles", or "Meta-bundles"). The CLAUDE.md mention is test-enforced: tests/docs/test_doc_consistency.py::TestBundleDocumentation fails for any bundle defined in .rhiza/template-bundles.yml that CLAUDE.md does not name.

  1. Run make validate

Before opening a PR, run the normal validation flow:

make validate

In this repository, make validate intentionally skips downstream-template validation because Rhiza itself has no .rhiza/template.yml. When you add a bundle here, also run the repository test suite so the new tests/bundles/ coverage actually executes.

  1. Open a PR

Include the new bundle directory, the .rhiza/template-bundles.yml entry, the documentation updates, and the matching tests/bundles/ coverage in one PR so reviewers can validate the bundle end to end.

Quick Review Checklist

Each step lists the gate that enforces it, so a miss is discovered here rather than as a surprise CI failure:

  • .rhiza/template-bundles.yml contains the bundle metadata with a description (gate: tests/bundles/test_template_bundles.py::TestTemplateBundles)
  • bundles/<name>/ exists, is non-empty, and owns no file claimed by another bundle (gate: tests/bundles/test_template_bundles.py::TestTemplateBundles)
  • Dependencies are declared with requires and reference existing bundles (gate: tests/bundles/test_template_bundles.py::TestTemplateBundles)
  • CLAUDE.md mentions the bundle in its overview (gate: tests/docs/test_doc_consistency.py::TestBundleDocumentation)
  • README.md's bundle tables include the bundle (no gate — reviewer responsibility)
  • Bundle × platform compatibility coverage picks the bundle up automatically from .rhiza/template-bundles.yml (gate: tests/bundles/test_bundle_matrix.py — nothing to add manually, but a matrix failure for <name> points at a YAML, ownership, or dependency problem in the new bundle)
  • tests/bundles/ covers the new bundle's synced output (focused test you write)
  • make validate and the full test suite have been run locally

Extension Points

1. Makefile Hooks

Hooks allow you to inject custom logic into standard workflows using double-colon syntax (::).

Available Hooks

Hook When It Runs Common Use Cases
pre-install Before make install Install system dependencies, validate environment
post-install After make install Additional setup, generate files, configure services
pre-sync Before template sync Backup files, validate state
post-sync After template sync Regenerate files, apply local patches
pre-validate Before project validation Custom checks, pre-validation setup
post-validate After project validation Additional validation steps
pre-bump Before version bump Update version in additional files
post-bump After version bump Generate changelogs, update documentation
pre-release Before creating release Final checks, build artifacts
post-release After creating release Deploy, notify team, update external systems

Hook Syntax

# In root Makefile (before include line)

# Single-line hook
post-install::
    @echo "Running custom setup..."

# Multi-line hook
post-install::
    @echo "Installing additional tools..."
    @uv run pip install my-private-package
    @echo "Setup complete!"

# Multiple hooks (they accumulate)
post-install::
    @./scripts/setup-database.sh

post-install::
    @./scripts/configure-services.sh

2. Custom Make Targets

Add new make targets in your root Makefile to create project-specific commands.

Basic Target

# In root Makefile (before include line)

##@ Custom Tasks
my-task: ## Description shown in make help
    @echo "Running my custom task..."
    @uv run python scripts/my_script.py

Target with Dependencies

##@ Custom Tasks
deploy: test docs ## Deploy application (runs tests and docs first)
    @echo "Deploying application..."
    @./scripts/deploy.sh

Target with Variables

##@ Custom Tasks
train: ## Train ML model (use ENV=prod for production)
    @echo "Training model in $(ENV) environment..."
    @uv run python scripts/train.py --env=$(ENV)

3. Environment Variables

Override default variables or add new ones.

In Root Makefile

# In root Makefile (before include line)

# Override default Python version
PYTHON_VERSION = 3.12

# Override coverage threshold
COVERAGE_FAIL_UNDER = 80

# Add custom variable
MY_API_URL = https://api.example.com

# Export for use in recipes
export MY_API_URL

# Include Rhiza
include .rhiza/rhiza.mk

In .rhiza/.env

# .rhiza/.env
DATABASE_URL=postgresql://localhost/mydb
API_KEY=your-api-key
LOG_LEVEL=DEBUG

These are automatically loaded by the Makefile.


Common Patterns

Pattern 1: Installing System Dependencies

Ensure system packages are available before running your application.

# Root Makefile

pre-install::
    @echo "Checking system dependencies..."
    @if ! command -v graphviz >/dev/null 2>&1; then \
        echo "Installing graphviz..."; \
        if command -v brew >/dev/null 2>&1; then \
            brew install graphviz; \
        elif command -v apt-get >/dev/null 2>&1; then \
            sudo apt-get update && sudo apt-get install -y graphviz; \
        else \
            echo "Please install graphviz manually."; \
            exit 1; \
        fi \
    fi

Pattern 2: Database Setup

Set up a database during installation.

# Root Makefile

post-install::
    @echo "Setting up database..."
    @uv run python scripts/init_db.py
    @echo "Running migrations..."
    @uv run alembic upgrade head

##@ Database
db-migrate: ## Create a new database migration
    @uv run alembic revision --autogenerate -m "$(MSG)"

db-upgrade: ## Apply database migrations
    @uv run alembic upgrade head

db-downgrade: ## Rollback last migration
    @uv run alembic downgrade -1

db-reset: ## Reset database (WARNING: destructive!)
    @echo "Resetting database..."
    @uv run python scripts/reset_db.py
    @$(MAKE) db-upgrade

Pattern 3: Configuration Files

Generate configuration files from templates.

# Root Makefile

post-install::
    @echo "Generating configuration files..."
    @if [ ! -f config/local.yaml ]; then \
        cp config/local.yaml.template config/local.yaml; \
        echo "Created config/local.yaml - please customize it"; \
    fi

##@ Configuration
config-validate: ## Validate configuration files
    @uv run python scripts/validate_config.py

config-show: ## Show current configuration
    @uv run python -c "from myapp.config import settings; print(settings.model_dump_json(indent=2))"

Pattern 4: Building Assets

Compile frontend assets or other build artifacts.

# Root Makefile

post-install::
    @echo "Building frontend assets..."
    @npm install
    @npm run build

##@ Build
build-assets: ## Build frontend assets
    @echo "Building assets..."
    @npm run build

watch-assets: ## Watch and rebuild assets on change
    @npm run watch

clean-assets: ## Clean built assets
    @rm -rf static/dist/

Pattern 5: Multi-Environment Support

Support different environments (dev, staging, production).

# Root Makefile

# Default environment
ENV ?= dev

##@ Deployment
deploy-dev: ## Deploy to development
    @$(MAKE) deploy ENV=dev

deploy-staging: ## Deploy to staging
    @$(MAKE) deploy ENV=staging

deploy-prod: ## Deploy to production
    @$(MAKE) deploy ENV=prod

deploy: test ## Deploy to $(ENV) environment
    @echo "Deploying to $(ENV) environment..."
    @uv run python scripts/deploy.py --env=$(ENV)

Pattern 6: Development Servers

Run development servers with auto-reload.

# Root Makefile

##@ Development
dev: ## Start development server with auto-reload
    @echo "Starting development server..."
    @uv run uvicorn myapp.main:app --reload --host 0.0.0.0 --port 8000

dev-worker: ## Start background worker
    @echo "Starting background worker..."
    @uv run celery -A myapp.worker worker --loglevel=info

dev-all: ## Start all development services
    @echo "Starting all services..."
    @$(MAKE) -j dev dev-worker

Pattern 7: Data Management

Tasks for managing data, seeds, fixtures.

# Root Makefile

##@ Data Management
seed-db: ## Seed database with sample data
    @echo "Seeding database..."
    @uv run python scripts/seed_data.py

import-data: ## Import data from file (use FILE=path/to/file)
    @echo "Importing data from $(FILE)..."
    @uv run python scripts/import_data.py $(FILE)

export-data: ## Export data to file (use FILE=path/to/file)
    @echo "Exporting data to $(FILE)..."
    @uv run python scripts/export_data.py $(FILE)

backup-db: ## Backup database
    @echo "Creating backup..."
    @mkdir -p backups
    @uv run python scripts/backup_db.py backups/backup-$$(date +%Y%m%d-%H%M%S).sql

Pattern 8: Code Generation

Generate boilerplate code from templates.

# Root Makefile

##@ Code Generation
new-model: ## Create new model (use NAME=ModelName)
    @echo "Generating model $(NAME)..."
    @uv run python scripts/generate_model.py $(NAME)

new-api: ## Create new API endpoint (use NAME=endpoint_name)
    @echo "Generating API endpoint $(NAME)..."
    @uv run python scripts/generate_api.py $(NAME)

new-test: ## Create test file (use NAME=test_name)
    @echo "Generating test file $(NAME)..."
    @uv run python scripts/generate_test.py $(NAME)

Advanced Techniques

Conditional Logic

Execute different logic based on environment or system.

# Root Makefile

# Detect operating system
UNAME_S := $(shell uname -s)

pre-install::
ifeq ($(UNAME_S),Darwin)
    @echo "Installing macOS dependencies..."
    @brew install graphviz
else ifeq ($(UNAME_S),Linux)
    @echo "Installing Linux dependencies..."
    @sudo apt-get install -y graphviz
else
    @echo "Unsupported OS: $(UNAME_S)"
    @exit 1
endif

# Conditional based on environment variable
post-install::
ifdef CI
    @echo "Running in CI environment, skipping interactive setup"
else
    @echo "Running local setup..."
    @./scripts/interactive_setup.sh
endif

Parallel Execution

Run multiple tasks in parallel.

# Root Makefile

##@ Development
dev-all: ## Start all services in parallel
    @$(MAKE) -j4 dev-api dev-worker dev-frontend dev-db

dev-api:
    @uv run uvicorn myapp.api:app --reload

dev-worker:
    @uv run celery -A myapp.worker worker

dev-frontend:
    @npm run dev

dev-db:
    @docker-compose up postgres

Dynamic Targets

Generate targets dynamically.

# Root Makefile

# Define environments
ENVIRONMENTS := dev staging prod

# Generate deploy target for each environment
$(foreach env,$(ENVIRONMENTS),\
    $(eval deploy-$(env): ;\
        @echo "Deploying to $(env)..." ;\
        @./scripts/deploy.sh $(env)))

# Now you can run: make deploy-dev, make deploy-staging, make deploy-prod

Function Calls

Use make functions for reusable logic.

# Root Makefile

# Function to check if command exists
define check_command
    @command -v $(1) >/dev/null 2>&1 || { \
        echo "Error: $(1) is not installed"; \
        exit 1; \
    }
endef

##@ Validation
check-tools: ## Verify required tools are installed
    $(call check_command,docker)
    $(call check_command,kubectl)
    $(call check_command,helm)
    @echo "All required tools are installed ✓"

Including External Makefiles

Modularize large Makefiles.

# Root Makefile

# Include custom modules
-include makefiles/docker.mk
-include makefiles/kubernetes.mk
-include makefiles/terraform.mk

# Include Rhiza
include .rhiza/rhiza.mk

Then create makefiles/docker.mk:

# makefiles/docker.mk

##@ Docker
docker-dev: ## Run development environment in Docker
    @docker-compose -f docker-compose.dev.yml up

docker-prod: ## Build production Docker image
    @docker build -t myapp:$(VERSION) .

Real-World Examples

Example 1: Machine Learning Project

# Root Makefile for ML project

# ML-specific variables
DATA_DIR := data
MODELS_DIR := models
EXPERIMENT_NAME ?= default

post-install::
    @echo "Downloading datasets..."
    @uv run python scripts/download_data.py

##@ Machine Learning
train: ## Train model (use MODEL=model_name EXPERIMENT=name)
    @echo "Training $(MODEL) model..."
    @uv run python scripts/train.py \
        --model $(MODEL) \
        --experiment $(EXPERIMENT_NAME) \
        --data-dir $(DATA_DIR) \
        --output-dir $(MODELS_DIR)

evaluate: ## Evaluate model (use MODEL=model_name)
    @echo "Evaluating $(MODEL) model..."
    @uv run python scripts/evaluate.py \
        --model $(MODEL) \
        --data-dir $(DATA_DIR)/test

predict: ## Run inference (use MODEL=model_name INPUT=file)
    @uv run python scripts/predict.py \
        --model $(MODELS_DIR)/$(MODEL) \
        --input $(INPUT)

tune: ## Hyperparameter tuning (use MODEL=model_name)
    @echo "Tuning hyperparameters for $(MODEL)..."
    @uv run python scripts/tune.py --model $(MODEL)

experiment-track: ## Start MLflow tracking server
    @uv run mlflow server --host 0.0.0.0 --port 5000

##@ Data
download-data: ## Download datasets
    @uv run python scripts/download_data.py

preprocess-data: ## Preprocess raw data
    @uv run python scripts/preprocess.py \
        --input $(DATA_DIR)/raw \
        --output $(DATA_DIR)/processed

validate-data: ## Validate data quality
    @uv run python scripts/validate_data.py

# Include Rhiza
include .rhiza/rhiza.mk

Example 2: Web API Project

# Root Makefile for Web API project

# API configuration
API_HOST ?= 0.0.0.0
API_PORT ?= 8000
WORKERS ?= 4

post-install::
    @echo "Setting up database..."
    @uv run alembic upgrade head
    @echo "Loading fixtures..."
    @uv run python scripts/load_fixtures.py

##@ Development
dev: ## Start development server
    @uv run uvicorn myapi.main:app \
        --reload \
        --host $(API_HOST) \
        --port $(API_PORT)

dev-debug: ## Start development server with debugging
    @uv run python -m debugpy --listen 5678 \
        -m uvicorn myapi.main:app \
        --reload \
        --host $(API_HOST) \
        --port $(API_PORT)

shell: ## Open interactive Python shell with app context
    @uv run python scripts/shell.py

##@ Database
db-create: ## Create database
    @uv run python scripts/create_db.py

db-drop: ## Drop database (WARNING: destructive!)
    @echo "Are you sure? [y/N] " && read ans && [ $${ans:-N} = y ]
    @uv run python scripts/drop_db.py

db-migrate: ## Create new migration (use MSG="message")
    @uv run alembic revision --autogenerate -m "$(MSG)"

db-upgrade: ## Apply migrations
    @uv run alembic upgrade head

db-downgrade: ## Rollback migration
    @uv run alembic downgrade -1

db-seed: ## Seed database with test data
    @uv run python scripts/seed_db.py

##@ Production
start: ## Start production server
    @uv run gunicorn myapi.main:app \
        --workers $(WORKERS) \
        --worker-class uvicorn.workers.UvicornWorker \
        --bind $(API_HOST):$(API_PORT)

##@ API Testing
api-test: ## Run API tests
    @uv run pytest tests/api/ -v

api-load-test: ## Run load tests (use USERS=100 DURATION=60)
    @uv run locust \
        -f tests/load/locustfile.py \
        --users $(USERS) \
        --spawn-rate 10 \
        --run-time $(DURATION)s \
        --headless \
        --host http://localhost:$(API_PORT)

# Include Rhiza
include .rhiza/rhiza.mk

Example 3: Data Pipeline Project

# Root Makefile for data pipeline project

# Pipeline configuration
AIRFLOW_HOME := $(CURDIR)/airflow
export AIRFLOW_HOME

post-install::
    @echo "Initializing Airflow..."
    @uv run airflow db init
    @uv run airflow users create \
        --username admin \
        --password admin \
        --firstname Admin \
        --lastname User \
        --role Admin \
        --email admin@example.com

##@ Airflow
airflow-webserver: ## Start Airflow webserver
    @uv run airflow webserver --port 8080

airflow-scheduler: ## Start Airflow scheduler
    @uv run airflow scheduler

airflow-worker: ## Start Airflow worker
    @uv run airflow celery worker

airflow: ## Start all Airflow components
    @$(MAKE) -j3 airflow-webserver airflow-scheduler airflow-worker

##@ Pipelines
list-dags: ## List all DAGs
    @uv run airflow dags list

trigger-dag: ## Trigger DAG (use DAG=dag_id)
    @uv run airflow dags trigger $(DAG)

test-dag: ## Test DAG (use DAG=dag_id)
    @uv run airflow dags test $(DAG) $$(date +%Y-%m-%d)

backfill: ## Backfill DAG (use DAG=dag_id START=YYYY-MM-DD END=YYYY-MM-DD)
    @uv run airflow dags backfill $(DAG) \
        --start-date $(START) \
        --end-date $(END)

##@ Data
validate-schema: ## Validate data schemas
    @uv run python scripts/validate_schemas.py

check-data-quality: ## Run data quality checks
    @uv run python scripts/check_quality.py

# Include Rhiza
include .rhiza/rhiza.mk

Best Practices

1. Documentation

  • Add comments to explain complex logic
  • Use ## syntax for target descriptions (they appear in make help)
  • Group related targets with ##@ section headers
  • Document variables at the top of your Makefile
# Root Makefile

# Configuration variables
API_HOST ?= 0.0.0.0  # Host for API server
API_PORT ?= 8000     # Port for API server
LOG_LEVEL ?= info    # Logging level (debug, info, warning, error)

##@ API Server
dev: ## Start development server (use API_PORT=8080 to override port)
    @echo "Starting API on $(API_HOST):$(API_PORT)..."
    @uv run uvicorn app.main:app --host $(API_HOST) --port $(API_PORT) --log-level $(LOG_LEVEL)

2. Error Handling

  • Check for required variables
  • Validate prerequisites
  • Provide helpful error messages
##@ Deployment
deploy: ## Deploy to server (requires SERVER and ENV variables)
ifndef SERVER
    $(error SERVER is not set. Use: make deploy SERVER=prod-01 ENV=production)
endif
ifndef ENV
    $(error ENV is not set. Use: make deploy SERVER=prod-01 ENV=production)
endif
    @echo "Deploying to $(SERVER) in $(ENV) environment..."
    @./scripts/deploy.sh $(SERVER) $(ENV)

check-docker:
    @command -v docker >/dev/null 2>&1 || { \
        echo "Error: docker is not installed"; \
        echo "Install from: https://docs.docker.com/get-docker/"; \
        exit 1; \
    }

3. Idempotency

Make targets idempotent (safe to run multiple times).

post-install::
    @echo "Creating config file..."
    @if [ ! -f config/local.yaml ]; then \
        cp config/local.yaml.template config/local.yaml; \
    else \
        echo "Config already exists, skipping..."; \
    fi

db-create:
    @echo "Creating database..."
    @uv run python -c "from myapp.db import create_db; create_db(if_not_exists=True)"

4. DRY Principle

Avoid repetition by using variables and functions.

# Bad: Repetitive
deploy-dev:
    @echo "Deploying to dev..."
    @./scripts/deploy.sh dev
    @./scripts/notify.sh dev

deploy-staging:
    @echo "Deploying to staging..."
    @./scripts/deploy.sh staging
    @./scripts/notify.sh staging

# Good: DRY
ENV ?= dev

deploy: ## Deploy to $(ENV) environment
    @echo "Deploying to $(ENV)..."
    @./scripts/deploy.sh $(ENV)
    @./scripts/notify.sh $(ENV)

deploy-dev: ENV=dev
deploy-dev: deploy

deploy-staging: ENV=staging
deploy-staging: deploy

5. Testing Custom Targets

Test your custom targets work correctly.

##@ Testing
test-makefile: ## Test custom make targets
    @echo "Testing custom targets..."
    @$(MAKE) print-API_HOST
    @$(MAKE) print-API_PORT
    @echo "Targets exist: deploy, dev, db-migrate"
    @echo "All tests passed ✓"

6. Use Silent Prefix

Use @ to suppress command echoing for cleaner output.

# Bad: Noisy output
deploy:
    echo "Starting deployment..."
    ./scripts/deploy.sh
    echo "Deployment complete!"

# Good: Clean output
deploy:
    @echo "Starting deployment..."
    @./scripts/deploy.sh
    @echo "Deployment complete!"

7. Provide Defaults

Provide sensible defaults for variables.

# Defaults with override capability
HOST ?= localhost
PORT ?= 8000
ENV ?= dev
LOG_LEVEL ?= info

##@ Development
dev: ## Start development server
    @uv run uvicorn app:main --host $(HOST) --port $(PORT) --log-level $(LOG_LEVEL)

Troubleshooting

For sync-specific failure modes and recovery commands, see docs/troubleshooting.md.

Issue: Hook Not Running

Problem: Your post-install hook doesn't seem to execute.

Solution: Ensure you're using double-colon syntax and it's defined before the include line:

# Root Makefile

# ✅ Correct
post-install::
    @echo "Running custom setup..."

include .rhiza/rhiza.mk

Issue: Variable Not Recognized

Problem: Variable defined in Makefile not available in target.

Solution: Define variables before the include line and use export if needed:

# Root Makefile

# Define before include
MY_VAR = value
export MY_VAR  # Export if needed in sub-shells

include .rhiza/rhiza.mk

target:
    @echo "Variable: $(MY_VAR)"

Issue: Target Not in Help

Problem: Custom target doesn't appear in make help.

Solution: Add ## comment and ensure it comes after the target name and colon:

##@ Custom Tasks
my-target: ## This description will appear in help
    @echo "Running target..."

Issue: Circular Dependency

Problem: Get error about circular dependency.

Solution: Check for dependency loops:

# ❌ Bad: Circular dependency
a: b
b: a

# ✅ Good: Linear dependency
a: b
b: c
c:
    @echo "Base target"

Issue: Command Not Found in CI

Problem: Command works locally but fails in CI.

Solution: Check if command is available and install if needed:

pre-install::
ifdef CI
    @echo "Installing CI dependencies..."
    @apt-get update && apt-get install -y build-essential
endif

CodeQL Configuration

The CodeQL workflow (.github/workflows/rhiza_codeql.yml) performs security analysis on your code. However, CodeQL requires GitHub Advanced Security, which is:

  • Available for free on public repositories
  • ⚠️ Requires GitHub Enterprise license for private repositories

Automatic Behavior

By default, the CodeQL workflow:

  • Runs automatically on public repositories
  • Skips automatically on private repositories (unless you have Advanced Security)

Controlling CodeQL

You can override the default behavior using a repository variable:

  1. Go to your repository → SettingsSecrets and variablesActionsVariables tab
  2. Create a new repository variable named CODEQL_ENABLED
  3. Set the value:
  4. true - Force CodeQL to run (use if you have Advanced Security on a private repo)
  5. false - Disable CodeQL entirely (e.g., if it's causing issues)

For private repositories with Advanced Security enabled:

gh variable set CODEQL_ENABLED --body "true"

For users without Advanced Security, no action is needed. To disable it completely:

gh variable set CODEQL_ENABLED --body "false"

Or remove the workflow file entirely:

git rm .github/workflows/rhiza_codeql.yml
git commit -m "Remove CodeQL workflow"

Documentation Customization

The API documentation includes a logo in the sidebar. Override the default logo (assets/rhiza-logo.svg) by setting the LOGO_FILE variable in your Makefile or local.mk:

LOGO_FILE := assets/my-custom-logo.png

Custom pdoc Templates

Customize the look and feel of the API documentation by providing your own Jinja2 templates. Place custom templates in the book/pdoc-templates directory.

For example, to override the main module template, create book/pdoc-templates/module.html.jinja2.

See the pdoc documentation on templates for full details on overriding specific parts of the documentation.

For more details on customizing the documentation book, see BOOK.md.


See Also