feat: support rendering message template with Jinja

Add config setting to enable Jinja templates

Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
Christopher Arndt 2023-07-24 01:07:32 +02:00
parent 92d85d31bf
commit d6e24aa359
3 changed files with 48 additions and 16 deletions

View File

@ -1,5 +1,5 @@
FROM python:3.11-alpine
RUN python3 -m pip --no-cache-dir install bleach markdown matrix-nio
RUN python3 -m pip --no-cache-dir install bleach jinja2 markdown matrix-nio
ADD matrixchat-notify.py /bin/
ADD matrixchat-notify-config.json /etc/
RUN chmod +x /bin/matrixchat-notify.py

View File

@ -40,7 +40,7 @@ steps:
* `allowed_tags` *(default:* [`DEFAULT_ALLOWED_TAGS`]*)*
List or set or string with comma-separated list of HTML tag names. HTML
tags not included, will be stripped from the HTML output generated by
tags not included will be stripped from the HTML output generated by
rendering a Markdown message template.
Note that the default list does not include any tags, which allow to load
@ -64,14 +64,28 @@ steps:
The Matrix homeserver URL.
* `jinja`
If set to `yes`, `y`, `true`, `t`, `on` or `1`, the message template is
rendered with the [Jinja] templating engine (instead of performing simple
placeholder substitution). The template context is controlled by the
`pass_environment` setting, same as with non-Jinja templates, but
placeholders use a different syntax (example: `{{DRONE_REPO}}`), so the
`template` setting should be changed to be a valid Jinja2 template string
when this is enabled.
Using this feature requires the `jinja2` Python module to be available
(it is installed by default in the plugin's docker image).
* `markdown`
If set to `yes`, `y`, `true` or `on`, the message resulting from template
substtution is considered to be in Markdown format and will be rendered to
HTML and sent as a formatted message with `org.matrix.custom.html` format.
If set to `yes`, `y`, `true`, `t`, `on` or `1`, the message resulting from
template substtution is considered to be in Markdown format and will be
rendered to HTML and sent as a formatted message with the format set to
`org.matrix.custom.html`.
Using this feature requires the `markdown` and `bleach` Python modules to
be available (the plugin's docker image has them installed).
be available (they are installed by default in the plugin's docker image).
* `markdown_extensions` *(default:* `admonition, extra, sane_lists, smarty`)
@ -98,9 +112,9 @@ steps:
* `template` *(default:* `${DRONE_BUILD_STATUS}`*)*
The message template. Valid placeholders of the form `${PLACEHOLDER}` will
be substituted with the values of the matching environment variables
(subject to filtering according to the `pass_environment` setting).
The message template. Valid placeholders (example: `${DRONE_REPO}`) will be
substituted with the values of the matching environment variables (subject
to filtering according to the `pass_environment` setting).
See this [reference] for environment variables available in drone.io CI
pipelines.
@ -114,6 +128,7 @@ steps:
[`DEFAULT_ALLOWED_TAGS`]: ./matrixchat-notify.py#L34
[allowed attributes]: https://bleach.readthedocs.io/en/latest/clean.html#allowed-attributes-attributes
[drone.io]: https://drone.io/
[jinja]: https://jinja.palletsprojects.com/
[list of extensions]: https://python-markdown.github.io/extensions/
[plugin]: https://docs.drone.io/plugins/overview/
[reference]: https://docs.drone.io/pipeline/environment/reference/

View File

@ -5,6 +5,7 @@ Requires:
* <https://pypi.org/project/matrix-nio>
* Optional: <https://pypi.org/project/bleach/>
* Optional: <https://pypi.org/project/Jinja2/>
* Optional: <https://pypi.org/project/markdown/>
"""
@ -73,6 +74,7 @@ SETTINGS_KEYS = (
"deviceid",
"devicename",
"homeserver",
"jinja",
"markdown",
"markdown_extensions",
"pass_environment",
@ -86,7 +88,7 @@ log = logging.getLogger(PROG)
def tobool(s):
try:
return strtobool(s)
return strtobool(str(s))
except ValueError:
return False
@ -156,15 +158,15 @@ async def send_notification(config, message):
await client.close()
def render_message(config):
pass_environment = config.get("pass_environment", "")
def get_template_context(config):
pass_environment = config.get("pass_environment", [])
if not isinstance(pass_environment, list):
pass_environment = [pass_environment]
patterns = []
for value in pass_environment:
# expand any comma-separetd names/patterns
# expand any comma-separated names/patterns
if "," in value:
patterns.extend([p.strip() for p in value.split(",") if p.strip()])
else:
@ -176,8 +178,23 @@ def render_message(config):
for pattern in patterns:
filtered_names.update(fnmatch.filter(env_names, pattern))
context = {name: os.environ[name] for name in tuple(filtered_names)}
return {name: os.environ[name] for name in tuple(filtered_names)}
def render_message(config):
context = get_template_context(config)
template = config.get("template", DEFAULT_TEMPLATE)
if tobool(config.get("jinja")):
try:
from jinja2.sandbox import SandboxedEnvironment
env = SandboxedEnvironment()
return env.from_string(template).render(context)
except Exception as exc:
log.error("Could not render Jinja2 template: %s", exc)
return template
else:
return Template(template).safe_substitute(context)