Jinja2 Templating System¶
Imbi Automations uses Jinja2 as its template engine to provide dynamic content generation across workflows, prompts, and file actions. This document describes the templating system, available context variables, and usage examples.
Overview¶
The templating system enables:
- Dynamic workflow prompts with project-specific context
- Template-based file generation with full project metadata
- AI prompt customization using project facts and repository data
- Pull request and commit message generation
- Variable substitution in shell commands and Docker operations
Template Context¶
All templates receive a WorkflowContext
object that provides access to:
Core Context Variables¶
workflow
(Workflow)¶
Complete workflow definition and configuration.
Available fields:
workflow.configuration.name
- Workflow nameworkflow.configuration.description
- Workflow descriptionworkflow.configuration.prompt
- Workflow-level prompt URL/pathworkflow.configuration.git.*
- Git configuration (checkout, depth, branch)workflow.configuration.github.*
- GitHub settings (create_pull_request)workflow.configuration.filter.*
- Workflow filter criteria
Example:
imbi_project
(ImbiProject)¶
Complete project metadata from Imbi project management system.
Core fields:
imbi_project.id
- Project ID (integer)imbi_project.name
- Human-readable project nameimbi_project.slug
- URL-safe project identifier (kebab-case)imbi_project.description
- Project description (may be None)imbi_project.namespace
- Project namespace nameimbi_project.namespace_slug
- Namespace slugimbi_project.project_type
- Human-readable project typeimbi_project.project_type_slug
- Project type slugimbi_project.imbi_url
- URL to project in Imbi
Project metadata:
imbi_project.dependencies
- List of dependent project IDsimbi_project.environments
- List of environment names (e.g.,["development", "production"]
)imbi_project.facts
- Dictionary of project facts (key-value pairs)imbi_project.identifiers
- External system identifiers (GitHub, GitLab, etc.)imbi_project.links
- Dictionary of external linksimbi_project.urls
- Dictionary of project URLsimbi_project.project_score
- Project health score
Example:
Project: {{ imbi_project.name }}
Slug: {{ imbi_project.slug }}
Type: {{ imbi_project.project_type }}
{% if imbi_project.description %}
Description: {{ imbi_project.description }}
{% endif %}
{% if imbi_project.facts %}
Facts:
{% for key, value in imbi_project.facts.items() %}
- {{ key }}: {{ value }}
{% endfor %}
{% endif %}
github_repository
(GitHubRepository | None)¶
GitHub repository metadata (only available when GitHub identifier exists).
Core fields:
github_repository.id
- Repository IDgithub_repository.name
- Repository namegithub_repository.full_name
- Full name (org/repo)github_repository.owner.login
- Owner usernamegithub_repository.private
- Boolean: is privategithub_repository.description
- Repository descriptiongithub_repository.html_url
- GitHub web URLgithub_repository.ssh_url
- SSH clone URLgithub_repository.clone_url
- HTTPS clone URL
Additional fields:
github_repository.fork
- Boolean: is forkgithub_repository.created_at
- Creation timestampgithub_repository.updated_at
- Last update timestampgithub_repository.pushed_at
- Last push timestampgithub_repository.size
- Repository size in KBgithub_repository.stargazers_count
- Star countgithub_repository.watchers_count
- Watcher countgithub_repository.language
- Primary languagegithub_repository.has_issues
- Boolean: issues enabledgithub_repository.has_projects
- Boolean: projects enabledgithub_repository.has_wiki
- Boolean: wiki enabledgithub_repository.archived
- Boolean: is archivedgithub_repository.disabled
- Boolean: is disabledgithub_repository.default_branch
- Default branch name
Example:
{% if github_repository %}
Repository: {{ github_repository.full_name }}
URL: {{ github_repository.html_url }}
Language: {{ github_repository.language }}
Default Branch: {{ github_repository.default_branch }}
{% endif %}
working_directory
(pathlib.Path | None)¶
Absolute path to the temporary working directory containing the cloned repository.
Example:
starting_commit
(str | None)¶
Git commit SHA of the repository HEAD when workflow execution started.
Example:
Custom Template Functions¶
The templating system provides custom functions accessible within templates:
extract_image_from_dockerfile(dockerfile_path)
¶
Extracts the base Docker image from a Dockerfile.
Parameters:
dockerfile_path
- Path to Dockerfile (supportsrepository:///
URL scheme)
Returns: Base image name (e.g., python:3.12-slim
)
Example:
Usage in workflow:
[[actions]]
name = "extract-constraints"
type = "docker"
command = "extract"
image = "{{ extract_image_from_dockerfile('repository:///Dockerfile') }}"
source = "/tmp/constraints.txt"
destination = "extracted:///constraints.txt"
Template Usage¶
1. Claude Action Prompts¶
Claude actions use Jinja2 templates for AI prompts:
[[actions]]
name = "standardize-dunder-init"
type = "claude"
prompt = "prompts/init.md.j2"
validation_prompt = "prompts/validate-init.md.j2"
Example prompt template (prompts/init.md.j2
):
# Python Package __init__.py Version Standardization
Update the package's `__init__.py` file to standardize version handling.
## Context Variables
- Project name: `{{ imbi_project.name }}`
- Package name: `{{ imbi_project.slug }}`
- Project description: `{{ imbi_project.description }}`
## Current Facts
{% if imbi_project.facts %}
{% for key, value in imbi_project.facts.items() %}
- {{ key }}: {{ value }}
{% endfor %}
{% endif %}
## Repository Information
{% if github_repository %}
- Repository: {{ github_repository.full_name }}
- Primary language: {{ github_repository.language }}
{% endif %}
2. Template Actions¶
Template actions render entire files or directories:
[[actions]]
name = "render-config"
type = "template"
source_path = "templates/config.yaml.j2"
destination_path = "repository:///config/app.yaml"
Example template file (templates/config.yaml.j2
):
application:
name: {{ imbi_project.name }}
slug: {{ imbi_project.slug }}
version: "{{ imbi_project.facts.get('Version', '0.0.0') }}"
{% if imbi_project.environments %}
environments:
{% for env in imbi_project.environments %}
- {{ env }}
{% endfor %}
{% endif %}
{% if github_repository %}
repository:
url: {{ github_repository.html_url }}
default_branch: {{ github_repository.default_branch }}
{% endif %}
3. Shell Command Templating¶
Shell commands support inline Jinja2 templating:
[[actions]]
name = "update-version"
type = "shell"
command = "sed -i '' 's/version = .*/version = \"{{ imbi_project.facts.Version }}\"/' pyproject.toml"
4. Docker Image Extraction¶
Use extract_image_from_dockerfile()
in Docker actions:
[[actions]]
name = "extract-constraints"
type = "docker"
command = "extract"
image = "{{ extract_image_from_dockerfile('repository:///Dockerfile') }}"
source = "/tmp/constraints.txt"
destination = "extracted:///constraints.txt"
5. Pull Request Generation¶
Pull request summaries are generated from templates with commit context:
Template (prompts/pull-request-summary.md.j2
):
# {{ workflow.configuration.name }}
{{ workflow.configuration.description }}
## Project Details
- **Project**: {{ imbi_project.name }}
- **Type**: {{ imbi_project.project_type }}
- **Imbi URL**: {{ imbi_project.imbi_url }}
{% if github_repository %}
## Repository
- **Name**: {{ github_repository.full_name }}
- **Language**: {{ github_repository.language }}
{% endif %}
## Changes Summary
{{ summary }}
6. Directory Templates¶
Template actions can render entire directories recursively:
[[actions]]
name = "enforce-ci-scripts"
type = "template"
source_path = "templates/ci"
destination_path = "repository:///ci/"
All files in templates/ci/
are rendered with full context and written to repository:///ci/
.
Resource URL Schemes¶
The templating system supports custom URL schemes for path resolution:
repository:///
- Files in the cloned git repositoryworkflow:///
- Files in the workflow directoryextracted:///
- Files extracted during workflow executionfile:///
- Absolute file system paths
Example:
[[actions]]
name = "copy-gitignore"
type = "file"
command = "copy"
source = "workflow:///.gitignore"
destination = "repository:///.gitignore"
Jinja2 Configuration¶
The templating environment uses:
- autoescape=False
- No HTML escaping (templates are code, not HTML)
- undefined=jinja2.StrictUndefined
- Raises errors for undefined variables (fail fast)
This ensures templates fail early if variables are missing rather than silently producing incorrect output.
Common Patterns¶
Conditional Sections¶
{% if imbi_project.facts.get('Framework') %}
This project uses {{ imbi_project.facts.Framework }}.
{% endif %}
{% if github_repository and github_repository.language == 'Python' %}
Python-specific instructions...
{% endif %}
Iterating Over Collections¶
{% if imbi_project.environments %}
Environments:
{% for env in imbi_project.environments %}
- {{ env }}
{% endfor %}
{% endif %}
{% if imbi_project.facts %}
Project Facts:
{% for key, value in imbi_project.facts.items() %}
{{ key }}: {{ value }}
{% endfor %}
{% endif %}
Default Values¶
Project: {{ imbi_project.name }}
Description: {{ imbi_project.description or "No description available" }}
Version: {{ imbi_project.facts.get('Version', '0.0.0') }}
Multi-line Strings¶
"""
{{ imbi_project.name }}
{{ "=" * imbi_project.name|length }}
{{ imbi_project.description or "No description" }}
"""
Template Inheritance¶
While not commonly used in this system, Jinja2 supports template inheritance:
Best Practices¶
- Use strict undefined checking - Let templates fail on missing variables
- Provide defaults - Use
or
operator orget()
method for optional fields - Check for None - Many fields can be
None
, always check before accessing - Use descriptive variable names - Context is self-documenting
- Keep templates readable - Use whitespace and comments liberally
- Validate template output - Use validation prompts for Claude actions
- Test with multiple projects - Different project types have different facts
Debugging Templates¶
Enable Verbose Logging¶
Run workflows with verbose flag to see rendered templates:
Check Template Syntax¶
Use has_template_syntax()
to detect Jinja2 patterns:
from imbi_automations import prompts
if prompts.has_template_syntax(command):
# Command contains {{ }}, {% %}, or {# #}
rendered = prompts.render(context, command, **context.model_dump())
Common Errors¶
UndefinedError: 'None' has no attribute 'name'
- Check if object exists before accessing attributes
- Use conditional checks: {% if github_repository %}...{% endif %}
UndefinedError: 'dict object' has no attribute 'Programming_Language'
- Use .get()
method for dictionary access: imbi_project.facts.get('Programming Language')
- Facts use spaces, not underscores in keys
Template renders empty string
- Check that source file exists and is readable
- Verify URL scheme is correct (repository:///
, not repository://
)
Examples¶
Complete Claude Action Example¶
Workflow configuration:
Prompt template (prompts/update-readme.md.j2
):
# README Update Task
Update the README.md file for {{ imbi_project.name }}.
## Project Information
- **Name**: {{ imbi_project.name }}
- **Type**: {{ imbi_project.project_type }}
- **Description**: {{ imbi_project.description or "No description available" }}
{% if imbi_project.facts %}
## Project Facts
{% for key, value in imbi_project.facts.items() %}
- **{{ key }}**: {{ value }}
{% endfor %}
{% endif %}
{% if github_repository %}
## Repository
- **URL**: {{ github_repository.html_url }}
- **Language**: {{ github_repository.language }}
- **Default Branch**: {{ github_repository.default_branch }}
{% endif %}
## Task
Update the README.md to reflect the current project state, including:
1. Project name and description
2. Programming language and framework
3. Build and test instructions
4. Links to relevant documentation
Complete Template Action Example¶
Workflow configuration:
[[actions]]
name = "render-compose"
type = "template"
source_path = "templates/compose.yaml.j2"
destination_path = "repository:///compose.yaml"
Template file (templates/compose.yaml.j2
):
version: '3.8'
services:
{{ imbi_project.slug }}:
build:
context: .
dockerfile: Dockerfile
image: {{ imbi_project.slug }}:latest
container_name: {{ imbi_project.slug }}
environment:
- APP_NAME={{ imbi_project.name }}
- APP_SLUG={{ imbi_project.slug }}
{% if imbi_project.facts.get('Framework') %}
- FRAMEWORK={{ imbi_project.facts.Framework }}
{% endif %}
{% if imbi_project.project_type_slug in ['apis', 'web-applications'] %}
ports:
- "8080:8080"
{% endif %}
{% if imbi_project.facts.get('Database') %}
depends_on:
- database
{% endif %}
volumes:
- .:/app
{% if imbi_project.environments %}
# Configured environments: {{ imbi_project.environments|join(', ') }}
{% endif %}
{% if imbi_project.facts.get('Database') == 'PostgreSQL' %}
database:
image: postgres:15
environment:
- POSTGRES_DB={{ imbi_project.slug }}
- POSTGRES_USER=app
- POSTGRES_PASSWORD=secret
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
{% endif %}
See Also¶
- Jinja2 Documentation
- Workflow Configuration - Complete workflow configuration reference
- Actions Reference - Action implementations and usage