-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathREADME.md.template
233 lines (152 loc) · 6.52 KB
/
README.md.template
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
{{JINJA_TREE_STYLE1_GENERATED_COMMENT}}
# jinja-tree
## What is it?
`jinja-tree` is a CLI utility to process [jinja (jinja2)](https://jinja.palletsprojects.com/) templates
recursively in a directory tree.
It is very configurable and very easy to extend with its included plugin system.
The default behavior is to recursively search for files with a given extension (`.template` for example) and to process the context with [Jinja (Jinja2)](https://jinja.palletsprojects.com/) templates engine reading the context variables from:
- a configuration file
- environment variables
- dotenv files
Then, the processed content is written into another file with the same name/path but with the configured extension (`.template` by default) removed. The original file can also be deleted (but this is not the default behavior).
<details>
<summary>Full example about overall operation (in default mode)</summary>
Note: this is only the **default behavior** as you can tune this with your own plugins!
Let's imagine the following directory structure:
```
/foo/
/foo/README.md.template
/foo/bar/baz.py.template
/foo/bar/another.file
```
And execute `jinja-tree /foo` with the default configuration.
We get:
```
/foo/
/foo/README.md.template
/foo/README.md <= NEW FILE FROM README.md.template jinja2 processing
/foo/bar/baz.py.template
/foo/bar/baz.py <= NEW FILE FROM baz.py.template jinja2 processing
/foo/bar/another.file
```
</details>
## What's it for?
Your imagination is your limit 😅 but it's very useful for maintaining DRY documentation (for example `your-cli --help` output automatically updated in a markdown file), configuration files with default values read in code, including common blocks in different files...
**So it's a great tool for maintaining repositories in general.**
> [!TIP]
> Do you cant real-life examples? You can find some details about how we use it in this repository for:
>
> - [getting `jinja-tree --help` output automatically added (and updated) in this README]({{BASEURL}}docs/details-about-real-life-example1.md)
> - [getting a reference TOML configuration file rendered with default values read from code]({{BASEURL}}docs/details-about-real-life-example2.md)
> [!NOTE]
> Another "action" plugin will be soon 🕒 provided to bootstrap directory trees from templates (like with the [cookiecutter]({{BASEURL}}https://github.com/cookiecutter/cookiecutter) project).
## Features
#### 1️⃣ Easy to extend
`jinja-tree` includes a plugin system. You can override the default behavior with your own plugins.
There are two extension points:
- context plugins: to provide context variables to Jinja templates
- file plugins: to change the way how `jinja-tree` finds files to process (including target files)
See [this specification documentation page]({{BASEURL}}docs/details-about-plugins.md) for more details.
#### 2️⃣ Very configurable
`jinja-tree` is very configurable. You can configure global options via CLI options or a configuration file.
Plugins are configurable via the configuration file.
See [this specification documentation page]({{BASEURL}}docs/details-about-configuration.md) for more details.
#### 3️⃣ Embedded extensions
`jinja-tree` includes some extensions to Jinja templates engine:
- [to execute some commands (and get the corresponding output)]({{BASEURL}}jinja_tree/app/embedded_extensions/shell.py)
- [to parse JSON strings into Python objects)]({{BASEURL}}jinja_tree/app/embedded_extensions/from_json.py)
- ..
<details>
<summary>Usage examples</summary>
#### `shell` extension
{% raw %}
```jinja
{{ "date"|shell() }}
```
=> will render something like: `Sun Jan 28 15:11:44 CET 2024`
{% endraw %}
#### `from_json` extension
{% raw %}
```bash
export MYENV='["foo", "bar", "baz"]'
(
cat <<EOF
{% for item in MYENV|from_json() -%}
- {{ item }}
{% endfor %}
EOF
) | jinja-stdin
```
{% endraw %}
=> will render something like:
```
- foo
- bar
- bar
```
</details>
See [this directory]({{BASEURL}}jinja_tree/app/embedded_extensions/) for others
#### 4️⃣ Full Jinja / Jinja2 support (including "includes" and "inheritance")
`jinja-tree` has several options for Jinja "search paths". So you can use Jinja "includes" and "inheritance" features.
## Installation
`pip install jinja-tree`
> [!TIP]
> If you want to get a better readability of `jinja-tree` output (colors...), you can also use `pip install rich` to install
> this **optional** dependency.
> [!NOTE]
> A docker image is also available. You can use it to avoid any specific installation.
> See at the end of the "Usage" section for more details.
## Usage
### Main CLI
```
jinja-tree .
```
> [!NOTE]
> The `.` in the previous command in the "root directory" (the directory `jinja-tree` will explore recursively to find files to process). You can replace it with any directory you want. By using `.`, you will process all files in the current directory and its subdirectories.
<details>
<summary>Main CLI options</summary>
```
{{ "jinja-tree --help"|shell() }}
```
</details>
### Bonus CLI (if you want to process only one file but with the same behavior)
```bash
cat /path/to/your/file/to/process | jinja-stdin >/path/to/your/processed/file
```
or (if you want to process only a string):
{% raw %}
```console
$ export FOO=bar
$ echo "Hello {{FOO}}" | jinja-stdin
Hello bar
```
{% endraw %}
<details>
<summary>Bonus CLI options</summary>
```
{{ "jinja-stdin --help"|shell() }}
```
</details>
### Docker image
A docker image is also available. You can use it this way:
```bash
docker run -t -v $(pwd):/workdir --user=$(id -u) ghcr.io/fabien-marty/jinja-tree:latest /workdir
```
*(we mount the current directory in the `/workdir` directory in the container and execute `jinja-tree` in this `/workdir` directory)*
> [!WARNING]
> If you plan to use environment variables with the docker image, you will have to use (possibly multiple times) the `-e VAR=VALUE` option to pass them to the container.
> With docker, it's more practical to use a `.env` (dotenv) file as it will be automatically mounted in the container.
If you want to add some CLI options, you can add them like in this example:
```bash
docker run -t -v $(pwd):/workdir --user=$(id -u) ghcr.io/fabien-marty/jinja-tree:latest --verbose /workdir
```
*(we added `--verbose` just before the `/workdir` argument)*
<details>
<summary>If you want to use the `jinja-stdin` CLI with docker?</summary>
{% raw %}
```bash
echo "FOO {{ BAR }}" |docker run -i -v $(pwd):/workdir -e BAR=BAZ --user=$(id -u) --entrypoint jinja-stdin ghcr.io/fabien-marty/jinja-tree:latest
```
{% endraw %}
*(it will output `FOO BAZ`)*
</details>