Template Actions¶
⚠️ CRITICAL BUG: Template actions are currently broken. Context variables are not passed to templates, causing all variable references to fail with UndefinedError
. See Template Context section for details.
Template actions render Jinja2 templates with full workflow context, enabling dynamic file generation for configurations, documentation, and code files.
Configuration¶
[[actions]]
name = "action-name"
type = "template"
source_path = "template-file-or-directory"
destination_path = "output-location"
Fields¶
source_path (required)¶
Path to template file or directory. Can be:
- Single
.j2
template file - Directory containing
.j2
files (recursively rendered)
Type: ResourceUrl
(string path)
Conventions:
- Template files should use
.j2
extension - Directory paths should NOT include wildcards
- Relative to working directory by default
- Usually prefixed with
workflow:///
for workflow resources
destination_path (required)¶
Output location for rendered templates.
Type: ResourceUrl
(string path)
Behavior:
- For single file: Output file path
- For directory: Output directory (structure mirrored)
- Parent directories created automatically
- Existing files overwritten
Template Context¶
⚠️ KNOWN ISSUE: The current implementation does NOT pass context variables to templates. Template variables like {{ imbi_project.name }}
will raise UndefinedError
until this bug is fixed.
Expected variables (not currently available):
Variable | Type | Description |
---|---|---|
workflow |
Workflow |
Current workflow configuration |
imbi_project |
ImbiProject |
Complete Imbi project data |
github_repository |
GitHubRepository |
GitHub repo data (if applicable) |
working_directory |
Path |
Workflow execution directory |
starting_commit |
str |
Initial Git commit SHA |
Technical Details:
The template action calls prompts.render(self.context, source_path)
but does not pass **self.context.model_dump()
as kwargs, unlike shell and claude actions which do pass context variables. This means all template examples below will fail in the current implementation.
Examples¶
Single File Template¶
Workflow config:
[[actions]]
name = "generate-readme"
type = "template"
source_path = "workflow:///templates/README.md.j2"
destination_path = "repository:///README.md"
Template (templates/README.md.j2
):
# {{ imbi_project.name }}
{{ imbi_project.description }}
## Project Information
- **Type**: {{ imbi_project.project_type }}
- **Namespace**: {{ imbi_project.namespace }}
- **Imbi URL**: {{ imbi_project.imbi_url }}
{% if github_repository %}
## Repository
- **GitHub**: {{ github_repository.html_url }}
- **Default Branch**: {{ github_repository.default_branch }}
{% endif %}
## Installation
```bash
pip install {{ imbi_project.slug }}
Generated by {{ workflow.configuration.name }} on {{ now() }}
### Directory Template
**Workflow config:**
```toml
[[actions]]
name = "render-configs"
type = "template"
source_path = "workflow:///templates/configs"
destination_path = "repository:///config/"
Directory structure:
Result:
GitHub Actions Workflow Template¶
Workflow config:
[[actions]]
name = "generate-ci-workflow"
type = "template"
source_path = "workflow:///ci-template.yml.j2"
destination_path = "repository:///.github/workflows/ci.yml"
Template:
name: CI
on:
push:
branches: [ {{ github_repository.default_branch }} ]
pull_request:
branches: [ {{ github_repository.default_branch }} ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
{% if imbi_project.facts.get('Programming Language', '').startswith('Python') %}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '{{ imbi_project.facts['Programming Language'].split()[-1] }}'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[dev]
- name: Run tests
run: pytest tests/ -v
{% elif imbi_project.facts.get('Programming Language') == 'JavaScript' %}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
{% endif %}
Dockerfile Template¶
Workflow config:
[[actions]]
name = "generate-dockerfile"
type = "template"
source_path = "workflow:///Dockerfile.j2"
destination_path = "repository:///Dockerfile"
Template:
{% set python_version = imbi_project.facts.get('Programming Language', 'Python 3.12').split()[-1] %}
FROM python:{{ python_version }}-slim
LABEL maintainer="{{ imbi_project.namespace }}"
LABEL project="{{ imbi_project.slug }}"
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
{% if imbi_project.project_type == 'api' %}
CMD ["uvicorn", "{{ imbi_project.slug }}.main:app", "--host", "0.0.0.0", "--port", "8000"]
{% elif imbi_project.project_type == 'consumer' %}
CMD ["python", "-m", "{{ imbi_project.slug }}.consumer"]
{% else %}
CMD ["python", "-m", "{{ imbi_project.slug }}"]
{% endif %}
Jinja2 Template Features¶
Variables¶
Conditionals¶
{% if imbi_project.project_type == 'api' %}
API-specific content
{% elif imbi_project.project_type == 'consumer' %}
Consumer-specific content
{% else %}
Default content
{% endif %}
Loops¶
Filters¶
{{ imbi_project.name | upper }}
{{ imbi_project.slug | replace('-', '_') }}
{{ imbi_project.description | truncate(100) }}
Tests¶
{% if github_repository is defined %}
Has GitHub repository
{% endif %}
{% if imbi_project.facts %}
Has facts defined
{% endif %}
Comments¶
Common Patterns¶
Configuration File Generation¶
[[actions]]
name = "generate-app-config"
type = "template"
source_path = "workflow:///app.yaml.j2"
destination_path = "repository:///config/app.yaml"
Template:
application:
name: {{ imbi_project.slug }}
type: {{ imbi_project.project_type }}
{% if imbi_project.environments %}
environments:
{% for env in imbi_project.environments %}
{{ env.name }}:
url: {{ env.url }}
enabled: true
{% endfor %}
{% endif %}
database:
host: ${DB_HOST}
port: ${DB_PORT}
name: {{ imbi_project.slug | replace('-', '_') }}
Multi-File Template Directory¶
[[actions]]
name = "generate-all-configs"
type = "template"
source_path = "workflow:///templates"
destination_path = "repository:///config/"
Template directory:
Documentation Generation¶
[[actions]]
name = "generate-docs"
type = "template"
source_path = "workflow:///docs"
destination_path = "repository:///docs/"
Template:
# {{ imbi_project.name }} Documentation
## Overview
{{ imbi_project.description }}
## Quick Start
### Installation
```bash
pip install {{ imbi_project.slug }}
Usage¶
API Reference¶
{% if imbi_project.project_type == 'api' %}
The API is available at: https://{{ imbi_project.slug }}.example.com/api
{% endif %}
Contributing¶
Contributions welcome! See CONTRIBUTING.md in the repository for details.
Generated from template by {{ workflow.configuration.name }}
## Advanced Templates
### Accessing Nested Data
```jinja2
{# Access project facts #}
{% if 'Programming Language' in imbi_project.facts %}
Language: {{ imbi_project.facts['Programming Language'] }}
{% endif %}
{# Access GitHub repository details #}
{% if github_repository %}
Stars: {{ github_repository.stargazers_count }}
Forks: {{ github_repository.forks_count }}
{% endif %}
Template Inheritance¶
Base template (base.j2
):
# {{ imbi_project.name }}
{% block content %}
Default content
{% endblock %}
---
Generated by {{ workflow.configuration.name }}
Child template (readme.j2
):
{% extends "base.j2" %}
{% block content %}
## Description
{{ imbi_project.description }}
## Installation
pip install {{ imbi_project.slug }}
{% endblock %}
Macros¶
{% macro render_environment(env) %}
## {{ env.name | title }}
- URL: {{ env.url }}
- Active: {{ env.active | default(true) }}
{% endmacro %}
# Environments
{% for env in imbi_project.environments %}
{{ render_environment(env) }}
{% endfor %}
Custom Filters¶
{# Convert kebab-case to snake_case #}
{{ imbi_project.slug | replace('-', '_') }}
{# Convert to SCREAMING_SNAKE_CASE #}
{{ imbi_project.slug | replace('-', '_') | upper }}
{# Truncate long descriptions #}
{{ imbi_project.description | truncate(100, True, '...') }}
Path Resolution¶
Both source and destination support ResourceUrl schemes:
# Workflow templates to repository
[[actions]]
type = "template"
source_path = "workflow:///templates/"
destination_path = "repository:///config/"
# Specific file paths
[[actions]]
type = "template"
source_path = "workflow:///README.md.j2"
destination_path = "repository:///README.md"
# Extracted data with templates
[[actions]]
type = "template"
source_path = "workflow:///process-extracted.j2"
destination_path = "extracted:///processed/output.txt"
Error Handling¶
Template actions raise errors for:
- Missing source: Source file/directory doesn't exist
- Template syntax errors: Invalid Jinja2 syntax
- Undefined variables: Referenced variables not in context
- I/O errors: Permission denied, disk full, etc.
Handling Undefined Variables¶
Strict mode (default - raises error):
Graceful fallback:
Check before use:
Implementation Notes¶
- Templates rendered using Jinja2 with StrictUndefined by default
- BUG: Context variables NOT passed to templates (missing
**context.model_dump()
in render calls) .j2
extension NOT automatically removed from output filenames- Directory rendering is recursive via
source_path.rglob('*')
- Existing files overwritten without warning
- Parent directories created automatically with
mkdir(parents=True, exist_ok=True)
- File permissions NOT explicitly preserved (uses default umask)
- Template context is immutable during rendering
- All Jinja2 built-in filters and tests available (if context were passed)
- Only
extract_image_from_dockerfile
custom function added to globals
Implementation Location: src/imbi_automations/actions/template.py:24-82
Bug Details:
# Current broken implementation (line 52, 73):
prompts.render(self.context, source_path)
# Should be (like shell.py:130 and claude.py:130):
prompts.render(self.context, source_path, **self.context.model_dump())
Best Practices¶
- Use
.j2
extension: Makes templates easily identifiable - Provide defaults: Use
| default()
for optional values - Check existence: Test
is defined
before accessing optional data - Document templates: Add comments explaining template logic
- Test templates: Verify output with sample data before production
- Version control: Keep templates in workflow resources
- Validate output: Use shell actions to validate generated configs