Skip to content

Template Actions

Template actions render Jinja2 templates with full workflow context, enabling dynamic file generation for configurations, documentation, and code files.

Configuration

toml [[actions]] name = "action-name" type = "template" source_path = "template-file-or-directory" destination_path = "output-location"

Path Schemes

Template actions use path schemes for source_path and destination_path. See the Path Schemes guide for details on all available schemes.

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

Templates have access to all workflow context variables:

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

Examples

Single File Template

Workflow config: toml [[actions]] name = "generate-readme" type = "template" source_path = "workflow:///templates/README.md.j2" destination_path = "repository:///README.md"

Template (templates/README.md.j2): ```jinja2

{{ 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: workflow/templates/configs/ ├── app.yaml.j2 ├── database.yaml.j2 └── logging.yaml.j2

Result: repository/config/ ├── app.yaml ├── database.yaml └── logging.yaml

GitHub Actions Workflow Template

Workflow config: toml [[actions]] name = "generate-ci-workflow" type = "template" source_path = "workflow:///ci-template.yml.j2" destination_path = "repository:///.github/workflows/ci.yml"

Template: ```jinja2 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: toml [[actions]] name = "generate-dockerfile" type = "template" source_path = "workflow:///Dockerfile.j2" destination_path = "repository:///Dockerfile"

Template: ```jinja2 {% 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

jinja2 {{ imbi_project.name }} {{ imbi_project.slug }} {{ workflow.configuration.name }}

Conditionals

jinja2 {% if imbi_project.project_type == 'api' %} API-specific content {% elif imbi_project.project_type == 'consumer' %} Consumer-specific content {% else %} Default content {% endif %}

Loops

jinja2 {% for env in imbi_project.environments %} - {{ env.name }}: {{ env.url }} {% endfor %}

Filters

jinja2 {{ imbi_project.name | upper }} {{ imbi_project.slug | replace('-', '_') }} {{ imbi_project.description | truncate(100) }}

Tests

```jinja2 {% if github_repository is defined %} Has GitHub repository {% endif %}

{% if imbi_project.facts %} Has facts defined {% endif %} ```

Comments

jinja2 {# This is a comment - won't appear in output #} {{ imbi_project.name }} {# inline comment #}

Common Patterns

Configuration File Generation

toml [[actions]] name = "generate-app-config" type = "template" source_path = "workflow:///app.yaml.j2" destination_path = "repository:///config/app.yaml"

Template: ```jinja2 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

toml [[actions]] name = "generate-all-configs" type = "template" source_path = "workflow:///templates" destination_path = "repository:///config/"

Template directory: workflow/templates/ ├── app.yaml.j2 ├── database.yaml.j2 ├── logging.yaml.j2 └── monitoring.yaml.j2

Documentation Generation

toml [[actions]] name = "generate-docs" type = "template" source_path = "workflow:///docs" destination_path = "repository:///docs/"

Template: ```jinja2

{{ imbi_project.name }} Documentation

Overview

{{ imbi_project.description }}

Quick Start

Installation

bash pip install {{ imbi_project.slug }}

Usage

```python from {{ imbi_project.slug | replace('-', '_') }} import main

main() ```

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): ```jinja2

{{ imbi_project.name }}

{% block content %} Default content {% endblock %}


Generated by {{ workflow.configuration.name }} ```

Child template (readme.j2): ```jinja2 {% extends "base.j2" %}

{% block content %}

Description

{{ imbi_project.description }}

Installation

pip install {{ imbi_project.slug }} {% endblock %} ```

Macros

```jinja2 {% 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

```jinja2 {# 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:

```toml

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): jinja2 {{ undefined_variable }} {# Raises error #}

Graceful fallback: jinja2 {{ undefined_variable | default('fallback value') }} {{ undefined_variable | default('') }}

Check before use: jinja2 {% if undefined_variable is defined %} {{ undefined_variable }} {% endif %}

Implementation Notes

  • Templates rendered using Jinja2 with StrictUndefined by default
  • Context variables automatically passed via context.model_dump() in prompts.render()
  • .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
  • Custom template functions: extract_image_from_dockerfile, extract_package_name_from_pyproject, get_component_version, python_init_file_path, read_file, compare_semver

Implementation Location: src/imbi_automations/actions/template.py:24-101

Best Practices

  1. Use .j2 extension: Makes templates easily identifiable
  2. Provide defaults: Use | default() for optional values
  3. Check existence: Test is defined before accessing optional data
  4. Document templates: Add comments explaining template logic
  5. Test templates: Verify output with sample data before production
  6. Version control: Keep templates in workflow resources
  7. Validate output: Use shell actions to validate generated configs