Using Jinja with shell variables from the CLI
Overview
To quote the upstream documentation:
Jinja is a fast, expressive, extensible templating engine. Special placeholders in the template allow writing code similar to Python syntax. Then the template is passed data to render the final document. And more verbosely: A Jinja template is simply a text file. Jinja can generate any text-based format (HTML, XML, CSV, LaTeX, etc.). A Jinja template doesn’t need to have a specific extension: .html, .xml, or any other extension is just fine.
A template contains variables and/or expressions, which get replaced with values when a template is rendered; and tags, which control the logic of the template. The template syntax is heavily inspired by Django and Python.
Jinja is commonly encountered in Python-based projects like Ansible, where it’s frequently used to template configuration files, rendered with dynamic inputs from the Ansible inventory and/or roles and playbooks.
It can also be useful to template and render files with information from environment variables using the Jinja CLI. This is a really useful pattern to build reusable CI/CD pipeline elements that behave in standard ways but source certain varying data points from the environment or an invocation of a pipeline at runtime.
Uses
The ability to dynamically render templates based on data in environment variables will have a multitude of uses. Some of these might include:
-
Github actions pipelines with Github Environments (and Environment Variables and Secrets)
Github Environments provide a useful mechanism to centrally define common configuration items to each environment (or environment-type) but which may differ in value. The different configuration items are available to each actions pipeline runner based on the target environment.
Given a simple synthentic example as shown below:
environments/ ├── dev │ ├── web_instance_count: 2 │ ├── app_instance_count: 1 │ └── db_instance_count: 1 └── prod ├── web_instance_count: 10 ├── app_instance_count: 4 └── database_instance_count: 4
Pipeline runs in dev could access
WEB_INSTANCE_COUNT
from corresponding runners. Pipeline runs in prod could use the same variable but do something different based on the larger value.
How-to
-
Install the Jinja CLI module from Pip, this example is from a Fedora system, adjust accordingly:
$ mkdir -p .venv/jinjacli/ $ python -m venv .venv/jinjacli/ $ . ~/.venv/jinjacli/bin/activate $ python -m pip install --upgrade pip $ pip install jinja-cli
Note: There is an RPM-packaged Jinja CLI for Fedora but it doesn’t parse environment variables as usefully.
-
Create an example template file:
$ cat > /tmp/credential.yml <<EOF --- username: {{ APP_USER_NAME }} password: {{ APP_SECRET_PASSWORD }} EOF
-
Set some environment variables:
$ export APP_USER_NAME='sulaco' $ export APP_SECRET_PASSWORD='x3nom0rph' $ set | grep APP_ APP_SECRET_PASSWORD=x3nom0rph APP_USER_NAME=sulaco
-
Test render the example template with the example environment variables:
$ jinja -X 'APP_*' /tmp/credential.yml --- username: sulaco password: x3nom0rph
Note: For a real pipeline with a credential we’d use a masked secret to ensure it remains unlogged.
Further reading
- https://jinja.palletsprojects.com/en/3.1.x/
- https://jinja.palletsprojects.com/en/3.1.x/templates/
- https://pypi.org/project/jinja-cli/
- https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#about-environments
- https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-variables
- https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-secrets
- Escaping double curly braces inside a markdown code block in Jekyll
- Writing the endraw tag in Jekyll code blocks