Skip to content

feat: Jinja2 template engine in workflow templates #4595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 78 commits into
base: main
Choose a base branch
from

Conversation

korolenkowork
Copy link
Contributor

@korolenkowork korolenkowork commented Apr 19, 2025

Closes #4594

📑 Description

This PR added the whole power of Jinja2 templating to the KeepHQ workflow

✅ Checks

  • My pull request adheres to the code style of this project
  • I have updated the documentation as required
  • All the tests have passed

✨ Features

  • Users can now choose their preferred template engine for workflows: Jinja2 or Mustache
  • Mustache remains the default template engine
  • Introduced a syntax validator that raises a RenderError if invalid syntax is used for the selected engine
  • If an incorrect templating engine is specified in the YAML, a ValueError is raised

🛠 Improvements

  • Mustache (via Chevrone) now uses a tracking dictionary to log missing keys in the context, implemented in a thread-safe way.

⚠️ Known Limitations

  • During Jinja2 rendering, only missing top-level keys can be detected — full key paths cannot be tracked.
    Example: {{ alert.namespase }} will report {{ namespace }} as missing, but due to how Jinja2's Undefined class works, it cannot identify the full path to the missing value.

  • It’s not possible to use a tracking dictionary for Jinja2 in the same way as Mustache, because Jinja2 internally converts the context to a plain dict during rendering.

Copy link

vercel bot commented Apr 19, 2025

@EnotShow is attempting to deploy a commit to the KeepHQ Team on Vercel.

A member of the Team first needs to authorize it.

@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. Documentation Improvements or additions to documentation Feature A new feature labels Apr 19, 2025
@korolenkowork korolenkowork changed the title Feature/jinja template engine in workflow templates feature: Jinja2 template engine in workflow templates Apr 19, 2025
@korolenkowork korolenkowork changed the title feature: Jinja2 template engine in workflow templates feat: Jinja2 template engine in workflow templates Apr 19, 2025
@korolenkowork korolenkowork marked this pull request as draft April 19, 2025 18:22
@korolenkowork korolenkowork marked this pull request as ready for review April 19, 2025 22:53
@shahargl
Copy link
Member

@EnotShow lot of unit tests are failing, can you fix that? :)

Copy link
Member

@shahargl shahargl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests failing

@talboren
Copy link
Member

Also a quick question about backwards compatibility - not sure if I understand if this will still support mustache or we're replacing to Jinja completely? Maybe we should support templating=mustahce or templating=jinja in the top of the workflow to decide which engine we want to use. Will the keep. functions work?

@korolenkowork korolenkowork marked this pull request as draft April 20, 2025 08:46
@korolenkowork
Copy link
Contributor Author

@talboren

  1. This PR fully replaces Chevron with Jinja2 in workflows. I’ve proposed sticking with it, since once this is done, the previous workflows need to remain fully compatible. But it makes sense to proceed cautiously, especially since we don’t yet know all the potential issues this might introduce. So if the team decides it has to be done, I’m happy to go ahead with it.

  2. Yes, keep functions working perfectly!

skynetigor
skynetigor previously approved these changes May 1, 2025
Copy link
Contributor

@skynetigor skynetigor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@korolenkowork
Copy link
Contributor Author

Hi @talboren, @shahargl!
All tests are passed, and I received approval from @skynetigor, so it seems ready for merge!

Comment on lines 31 to 64
def validate_template_syntax(self, template: str) -> bool:
"""
Checks whether the template matches the expected template engine.

Args:
template (str): The template text to be checked.

Returns:
bool: True if the template matches the expected engine.

Raises:
RenderException: If the template contains syntax from a different engine.
"""
# Patterns specific to Jinja2
jinja2_patterns = {
'statement': r'{%[^%}]+%}', # {% if something %}
'comment': r'{#[^#}]+#}', # {# comment #}
'filters': r'\{\{[^}]+\|[^}]+\}\}', # {{ variable|filter }}
'blocks': r'{%\s*(end)?(if|for|block|macro|set)[^%}]*%}', # {% endif %}, {% endfor %}, etc.
}

# Patterns specific to Mustache
mustache_patterns = {
'section': r'{{#[^}]+}}', # {{#section}}
'inverted': r'{{(\^|\^!)[^}]+}}', # {{^section}} or {{^!section}}
'end_section': r'{{/[^}]+}}', # {{/section}}
'unescaped': r'{{{[^}]+}}}', # {{{ variable }}}
'comment': r'{{![^}]+}}', # {{! comment }}
'partial': r'{{>[^}]+}}', # {{> partial }}
}

has_jinja2 = False
has_mustache = False

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a better and simpler way to check jinja2 expressions,

e.g.:

from jinja2 import Environment, TemplateSyntaxError
 env = Environment()

# valid
In [4]: env.parse("Hello, {{ name }}!")
Out[4]: Template(body=[Output(nodes=[TemplateData(data='Hello, '), Name(name='name', ctx='load'), TemplateData(data='!')])])

# invalid
In [8]: a = """{% for item in items %}
   ...:   {{ item }}
   ...: {% endif %}"""

In [9]: env.parse(a)
-------------------------------------------------------------------
TemplateSyntaxError               Traceback (most recent call last)
Cell In[9], line 1
----> 1 env.parse(a)

File ~/git/keep/.venv/lib/python3.11/site-packages/jinja2/environment.py:616, in Environment.parse(self, source, name, filename)
    614     return self._parse(source, name, filename)
    615 except TemplateSyntaxError:
--> 616     self.handle_exception(source=source)

File ~/git/keep/.venv/lib/python3.11/site-packages/jinja2/environment.py:942, in Environment.handle_exception(self, source)
    937 """Exception handling helper.  This is used internally to either raise
    938 rewritten exceptions or return a rendered traceback for the template.
    939 """
    940 from .debug import rewrite_traceback_stack
--> 942 raise rewrite_traceback_stack(source=source)

File <unknown>:3, in template()

TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s a good approach, but my goal here isn’t to check whether the Jinja2 template is syntactically valid. Instead, I want to detect if the user accidentally used Mustache syntax when they selected Jinja2 as the template engine (or vice versa), and raise an informative error.

So it's more about catching cross-engine mistakes — for example, using {{#section}}...{{/section}} inside a Jinja2 context, or {% if ... %} inside a Mustache one.

That said, I agree this logic could probably be simplified. I’ll take another look and try to clean it up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👑

Copy link
Member

@shahargl shahargl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems almost done, see comments.

and if you can add more tests it will be great <3 just a big change so want to make sure we are ok.

@shahargl
Copy link
Member

shahargl commented May 5, 2025

@EnotShow let me know when you want the final review + merging

@korolenkowork
Copy link
Contributor Author

@talboren This is ready for review, but I ran into an issue with Jinja2.

Jinja2 doesn't allow hyphens in identifier names when accessed as attributes in templates. To work around this, I implemented a function which replaces code like:

Pod status report:
{% for pod in steps.get-pods.results %}
Pod name: {{ pod.metadata.name }} || Namespace: {{ pod.metadata.namespace }} || Status: {{ pod.status.phase }}
{% endfor %}

with:

Pod status report:
{% for pod in steps['get-pods'].results %}
Pod name: {{ pod.metadata.name }} || Namespace: {{ pod.metadata.namespace }} || Status: {{ pod.status.phase }}
{% endfor %}

This change uses dictionary-style access, which is valid in Jinja2 for keys that contain hyphens. It seems to work, but I not sure if this solution is proper enough. What you think?

@talboren
Copy link
Member

@talboren This is ready for review, but I ran into an issue with Jinja2.

Jinja2 doesn't allow hyphens in identifier names when accessed as attributes in templates. To work around this, I implemented a function which replaces code like:

Pod status report:
{% for pod in steps.get-pods.results %}
Pod name: {{ pod.metadata.name }} || Namespace: {{ pod.metadata.namespace }} || Status: {{ pod.status.phase }}
{% endfor %}

with:

Pod status report:
{% for pod in steps['get-pods'].results %}
Pod name: {{ pod.metadata.name }} || Namespace: {{ pod.metadata.namespace }} || Status: {{ pod.status.phase }}
{% endfor %}

This change uses dictionary-style access, which is valid in Jinja2 for keys that contain hyphens. It seems to work, but I not sure if this solution is proper enough. What you think?

makes absolute sense! I'll try to review it ASAP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Documentation Improvements or additions to documentation Feature A new feature size:XXL This PR changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[➕ Feature]: Jinja template engine in workflow templates
4 participants