Rewrite the README
All checks were successful
CI / test (push) Successful in 1m6s
Lint / test (push) Successful in 1m6s

This commit is contained in:
Miguel Jacq 2026-06-20 20:17:28 +10:00
parent 13d1a2b972
commit f5de32b778
Signed by: mig5
GPG key ID: 03906B4110AAD3B8

319
README.md
View file

@ -4,55 +4,230 @@
<img src="https://git.mig5.net/mig5/jinjaturtle/raw/branch/main/jinjaturtle.svg" alt="JinjaTurtle logo" width="240" /> <img src="https://git.mig5.net/mig5/jinjaturtle/raw/branch/main/jinjaturtle.svg" alt="JinjaTurtle logo" width="240" />
</div> </div>
JinjaTurtle is a command-line tool to help you generate Jinja2 templates and JinjaTurtle is a command-line tool that helps turn existing native
Ansible inventory from a native configuration file (or files) of a piece of configuration files into reusable configuration-management templates.
software.
By default it generates:
- a **Jinja2** template; and
- an **Ansible defaults YAML** file containing the variables used by that
template.
It can also generate **ERB** templates and **Puppet Hiera-style YAML** data for
Puppet workflows.
JinjaTurtle does not try to replace configuration-management tools. Its job is
to speed up the boring first pass: take a real config file, discover the values
inside it, replace those values with variables, and write the corresponding
variable data beside the template.
## How it works ## How it works
* The config file(s) is/are examined JinjaTurtle examines a source config file and keeps the original structure as
* Parameter key names are generated based on the parameter names in the much as possible.
config file. In keeping with Ansible best practices, you pass a prefix
for the key names, which should typically match the name of your Ansible
role.
* A Jinja2 file is generated from the file with those parameter key names
injected as the `{{ variable }}` names.
* An Ansible inventory YAML file is generated with those key names and the
*values* taken from the original config file as the default vars.
By default, the Jinja2 template and the Ansible inventory are printed to For the default Jinja2/Ansible mode:
stdout. However, it is possible to output the results to new files.
1. The config file is parsed.
2. Variable names are generated from the config keys and paths.
3. Those variable names are prefixed with `--role-name`, which should usually
match your Ansible role name.
4. A Jinja2 template is generated with values replaced by `{{ variable }}`
expressions.
5. An Ansible defaults YAML file is generated with those variables and the
original values.
For ERB/Puppet mode:
1. The same parse/flatten/loop analysis is used.
2. An ERB template is generated with Puppet-style instance variables such as
`<%= @memory_limit %>`.
3. The variables file is written as Puppet Hiera-style data, such as
`php::memory_limit: 256M`.
4. If `--puppet-class` is supplied, that class name is used as the Hiera
namespace while `--role-name` remains the local variable prefix.
By default, the generated variable data and template are printed to stdout. Use
`--defaults-output` and `--template-output` to write them to files.
## Jinja2 / Ansible example
Say you have a `php.ini` file and you are inside an Ansible role with
`defaults/` and `templates/` directories:
```shell
jinjaturtle php.ini \
--role-name php \
--defaults-output defaults/main.yml \
--template-output templates/php.ini.j2
```
Given a source value such as:
```ini
memory_limit = 256M
```
JinjaTurtle will produce a template value like:
```jinja2
memory_limit = {{ php_memory_limit }}
```
and defaults data like:
```yaml
php_memory_limit: 256M
```
## ERB / Puppet example
Use `--template-engine erb` when you want Puppet ERB output:
```shell
jinjaturtle php.ini \
--role-name php \
--template-engine erb \
--defaults-output data/common.yaml \
--template-output templates/php.ini.erb
```
Given the same source value:
```ini
memory_limit = 256M
```
JinjaTurtle will produce an ERB template value like:
```erb
memory_limit = <%= @memory_limit %>
```
and Hiera-style data like:
```yaml
php::memory_limit: 256M
```
The `--defaults-output` option name is retained for CLI compatibility, but in
ERB mode the file is intended to be Puppet Hiera data rather than Ansible role
defaults.
JinjaTurtle does **not** generate Puppet classes or `file` resources. A Puppet
module, or another tool such as Enroll, should still declare the class
parameters and call the template, for example with Puppet's `template()`
function.
## Using `--puppet-class`
Most direct usage can simply use the same value for the role name and Puppet
class name:
```shell
jinjaturtle php.ini --role-name php --template-engine erb
```
This creates Hiera keys such as:
```yaml
php::memory_limit: 256M
```
and local ERB variables such as:
```erb
<%= @memory_limit %>
```
For generated systems, it can be useful to make `--role-name` more specific
while keeping the Hiera keys under the real Puppet class. For example:
```shell
jinjaturtle php.ini \
--role-name php_etc_php_ini \
--puppet-class php \
--template-engine erb
```
In that case the variable prefix can stay file-specific, while the Hiera data
is still written under `php::...`.
## What sort of config files can it handle? ## What sort of config files can it handle?
TOML, YAML, INI, JSON and XML-style config files should be okay. There are always JinjaTurtle supports common structured and semi-structured config formats:
going to be some edge cases in very complex files that are difficult to work
with, though, so you may still find that you need to tweak the results.
For XML and YAML files, JinjaTurtle will attempt to generate 'for' loops - TOML
and lists in the Ansible yaml if the config file looks homogenous enough to - YAML
support it. However, if it lacks the confidence in this, it will fall back to - INI-style files
using scalar-style flattened attributes. - JSON
- XML
- Postfix `main.cf`
- systemd unit files, such as `*.service`, `*.socket`, `*.timer`, and related
unit types
- OpenSSH-style config files, including `ssh_config`, `sshd_config`, and common
`*.conf` snippets detected as SSH config
You may need or wish to tidy up the config to suit your needs. For ambiguous extensions such as `*.conf`, JinjaTurtle uses lightweight content
sniffing. You can always force a handler with `--format`.
The goal here is really to *speed up* converting files into Ansible/Jinja2, For YAML, XML, TOML, INI-style, and other supported structured files,
but not necessarily to make it perfect. JinjaTurtle will attempt to generate loops when a repeated structure looks
homogeneous enough. If it is not confident, it falls back to flattened scalar
variables.
Some very complex files will still need manual cleanup. The goal is to speed up
conversion into Jinja2 or ERB templates, not to guarantee a perfect final module
without review.
## JSON, quoting, and type preservation
JinjaTurtle tries to preserve rendered config types.
For JSON, it uses JSON-aware expressions rather than plain string substitution.
This avoids generating invalid JSON such as:
```json
{"enabled": True}
```
when the correct rendered JSON should be:
```json
{"enabled": true}
```
In Jinja2 mode this uses Ansible-style JSON filters. In ERB mode it emits Ruby
JSON generation where required, for example:
```erb
<% require 'json' -%>
{
"enabled": <%= JSON.generate(@enabled) %>
}
```
That is expected for JSON ERB templates.
## Can I convert multiple files at once? ## Can I convert multiple files at once?
Certainly! Pass the folder name instead of a specific file name, and JinjaTurtle Yes. Pass a directory instead of a single file and JinjaTurtle will convert the
will convert any files it understands in that folder, storing all the various files it understands in that directory.
vars in the destination defaults yaml file, and converting each file into a
Jinja2 template per file type.
If all the files had the same 'type', there'll be one Jinja2 template. ```shell
jinjaturtle ./config-dir \
--role-name myrole \
--defaults-output defaults/main.yml \
--template-output templates/
```
You can also pass `--recursive` to recurse into subfolders. Use `--recursive` to recurse into subdirectories.
Note: when using 'folder' mode and multiple files of the same type, their vars In folder mode, variables for multiple files of the same type are grouped under
will be listed under an 'items' parent key in the yaml, each with an `id` key. an `items`-style structure in the generated YAML so that the resulting templates
You'll then want to use a `loop` in Ansible later, e.g: can be used with loops in Ansible.
For example:
```yaml ```yaml
- name: Render configs - name: Render configs
@ -93,9 +268,9 @@ sudo dnf upgrade --refresh
sudo dnf install jinjaturtle sudo dnf install jinjaturtle
``` ```
### From PyPi ### From PyPI
``` ```bash
pip install jinjaturtle pip install jinjaturtle
``` ```
@ -103,61 +278,81 @@ pip install jinjaturtle
Clone the repo and then run inside the clone: Clone the repo and then run inside the clone:
``` ```bash
poetry install poetry install
``` ```
### AppImage ### AppImage
Download the AppImage from the Releases and make it executable, and put it Download the AppImage from the Releases page, make it executable, and put it on
on your `$PATH`. your `$PATH`.
## How to run it
Say you have a `php.ini` file and you are in a directory structure like an
Ansible role (with subfolders `defaults` and `templates`):
```shell
jinjaturtle php.ini \
--role-name php \
--defaults-output defaults/main.yml \
--template-output templates/php.ini.j2
```
## Full usage info ## Full usage info
``` ```text
usage: jinjaturtle [-h] -r ROLE_NAME [-f {json,ini,toml,yaml,xml,postfix,systemd}] [-d DEFAULTS_OUTPUT] [-t TEMPLATE_OUTPUT] config usage: jinjaturtle [-h] [-r ROLE_NAME] [--recursive]
[-f {ini,json,toml,yaml,xml,postfix,systemd,ssh}]
[-d DEFAULTS_OUTPUT] [-t TEMPLATE_OUTPUT]
[--template-engine {jinja2,erb}]
[--puppet-class PUPPET_CLASS]
config
Convert a config file into Ansible inventory and a Jinja2 template. Convert a config file into an Ansible defaults file and Jinja2 template.
positional arguments: positional arguments:
config Path to the source configuration file. config Path to a config file OR a folder containing supported
config files. Supported: .toml, .yaml/.yml, .json,
.ini/.cfg/.conf, .xml, ssh_config/sshd_config
options: options:
-h, --help show this help message and exit -h, --help show this help message and exit
-r, --role-name ROLE_NAME -r, --role-name ROLE_NAME
Ansible role name, used as variable prefix (e.g. cometbft). Role name / variable prefix. In Jinja2 mode this is
-f, --format {ini,json,toml,xml} usually the Ansible role name. In ERB mode it is used
Force config format instead of auto-detecting from filename. as the local variable prefix. Defaults to jinjaturtle.
--recursive When CONFIG is a folder, recurse into subfolders.
-f, --format {ini,json,toml,yaml,xml,postfix,systemd,ssh}
Force config format instead of auto-detecting from
filename.
-d, --defaults-output DEFAULTS_OUTPUT -d, --defaults-output DEFAULTS_OUTPUT
Path to write defaults/main.yml. If omitted, default vars are printed to stdout. Path to write the generated variable YAML. If omitted,
it is printed to stdout.
-t, --template-output TEMPLATE_OUTPUT -t, --template-output TEMPLATE_OUTPUT
Path to write the Jinja2 config template. If omitted, template is printed to stdout. Path to write the generated config template. If omitted,
it is printed to stdout.
--template-engine {jinja2,erb}
Template syntax to generate. Defaults to jinja2. Use
erb for Puppet templates.
--puppet-class PUPPET_CLASS
Puppet class / Hiera namespace to use with
--template-engine erb. Defaults to --role-name.
``` ```
## Additional supported formats ## Additional supported formats
JinjaTurtle can also template some common "bespoke" config formats: JinjaTurtle also templates some common bespoke config formats:
- **Postfix main.cf** (`main.cf`) → `--format postfix` - **Postfix main.cf** (`main.cf`) → `--format postfix`
- **systemd unit files** (`*.service`, `*.socket`, etc.) → `--format systemd` - **systemd unit files** (`*.service`, `*.socket`, etc.) → `--format systemd`
- **OpenSSH config** (`ssh_config`, `sshd_config`, and detected snippets) →
`--format ssh`
For ambiguous extensions like `*.conf`, JinjaTurtle uses lightweight content sniffing; you can always force a specific handler via `--format`. For ambiguous extensions like `*.conf`, JinjaTurtle uses lightweight content
sniffing. You can always force a specific handler with `--format`.
## Relationship with Enroll
JinjaTurtle can be used directly, but it is also useful as a helper for tools
that generate configuration-management code.
Enroll can call JinjaTurtle when it is available on the `PATH`. In that setup,
Enroll decides where Puppet, Ansible, or Salt files belong, while JinjaTurtle
concentrates on converting harvested config files into templates plus variable
data.
## Found a bug, have a suggestion? ## Found a bug, have a suggestion?
You can e-mail me (see the pyproject.toml for details) or contact me on the Fediverse: You can e-mail me; see `pyproject.toml` for details. You can also contact me on
the Fediverse:
https://goto.mig5.net/@mig5 https://goto.mig5.net/@mig5