## inji

Render static jinja2 template files, optionally specifying parameters
contained in vars files.

Useful in CI/CD scenarios where
[DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)
configuration is necessary and the reduction of copy-paste duplication
can be done through templating/parameterization.

### Installation

NOTE: Support for Python2 is now dropped. Python3 is required.

```
pip install inji
```

### Usage

##### Render a lone Jinja2 template.

```
inji --template=jello-world.j2
```

##### Render a template passing vars from a YAML file.

```
inji --template=motd.j2 --vars-file=production.yaml
```

vars files must contain valid
[YAML documents](https://yaml.org/spec/1.2/spec.html#id2800132)
and can hold either simple
[scalars](https://yaml.org/spec/1.2/spec.html#id2760844)
or
[collections](https://yaml.org/spec/1.2/spec.html#id2759963).
Your jinja templates must reference these accordingly.

e.g.
```
$ cat vars.yaml
sage: victor
section: indigo

envoy:
  names:
    - alice
    - bob
    - chuck
  location: metaverse
  origin: world's end
```

```
$ cat hello-world.j2
Hello {{ envoy.names | join(', ') }}!!

I am {{ sage }}, the {{ section }} sage.

It seems you are at {{ envoy.location }} today and come from {{ envoy.origin }}.
```

```
$ inji -t hello-world.j2 -v vars.yaml
Hello alice, bob, chuck!!

I am victor, the indigo sage.

It seems you are at metaverse today and come from world's end.
```

##### Render a template using variables from multiple vars files

```
inji --template=nginx.conf.j2    \
      --vars-file=web-tier.yaml  \
      --vars-file=eu-west-1.yaml \
      --vars-file=prod.yaml  > /etc/nginx/sites-enabled/prod-eu-west-1.conf
```

This is especially useful in managing layered configuration.
i.e. variables from files specified later on the command-line
will be overlain over those defined earlier.

### Etymology

_inji_ is named in keeping of the UNIX tradition of short (memorable?)
 command names.

_inji_ (_/ɪndʒi:/_) also happens to be the Tamil word for Ginger.
In this case, it is a near-anagram of _Jinja_ which ostensibly is a homophone
of Ginger (Zingiber).

### Example

This is a very contrived example showing how to orient a `.gitlab-ci.yml`
towards business workflows - in this case a 3-stage Gitlab CI/CD deployment
pipeline to production.

```
$ cat .gitlab-ci.yml.vars
---
project:
  name: snowmobile
  id:   https://gitlab.com/snowslope/snowmobile.git
  url:  https://snowmobile.example.com/

deployer:
  image: snowmobile-deployer:latest

environments:

  - name: snowmobile-env_dev
    type: dev
    datacenter: eu-west-1
    url:  https://snowmobile-dev.env.example.com/
    only:
      - /^[0-9]+\-.*/  # Match feature branches that have
                       # a ticket number at the start of the branch name

  - name: snowmobile-env_stage
    type: stage
    datacenter: eu-west-2
    url:  https://snowmobile-stage.env.example.com/
    only:
      - master         # Deploy to stage only after merge request is complete

  - name: snowmobile-env_prod
    type: production
    datacenter: eu-west-1
    url:  https://snowmobile.env.example.com/
    only:
      - tags           # Only deploy tagged releases to production

...
```

```
$ cat .gitlab-ci.yml.j2
---

# >>>>>>>>>>>>>
# >> WARNING >>   This file is autogenerated!!
# >>>>>>>>>>>>>   All edits will be lost on the next update.

stages:
{% for env in environments %}
  - '{{ env.name }}:provision'
  - '{{ env.name }}:validate'
  - '{{ env.name }}:deploy'
  - '{{ env.name }}:test'
  - '{{ env.name }}:destroy'
{% endfor %}
  - 'docs:publish'

variables:
  project:             {{  project.name }}
  project_id:          '{{ project.id   }}'
  project_url:         {{  project.url  }}

{% for env in environments %}

# {{ env.type }} Run tenant provisioning, runner setup on shared runner
'provision:{{ env.name }}':
  stage: '{{ env.name }}:provision'
  environment:
    name: {{ env.type }}/$SITE/$CI_COMMIT_REF_NAME
    url:  {{ env.url }}
  variables:
    SITE:                {{ env.name }}
    CI_ENVIRONMENT_TYPE: {{ env.type }}
    DATACENTER:          {{ env.datacenter }}
    URL:                 {{ env.url }}
  image:  {{ deployer.image }}
  script:
    - snowmobile-ctl provision

  {% if env.only -%}
  only: {{ env.only }}
  {% endif %}

# {{ env.type }} Run deployment
'deploy:{{ env.name }}':
  stage: '{{ env.name }}:deploy'
  # ...
  script:
    - snowmobile-ctl deploy

# {{ env.type }} Run smoke tests
'test:{{ env.name }}':
  stage: '{{ env.name }}:test'
  # ...
  script:
    - snowmobile-ctl smoke-test

{% endfor %}

# vim:ft=yaml
...
```

To then update the `.gitlab-ci.yml`, run inji with the above.

```
$ inji -s strict -t .gitlab-ci.yml.j2 -v .gitlab-ci.yml.vars > .gitlab-ci.yml
```

NOTE: This is not ideal as edits to the above files are not automatically
reflected in `.gitlab-ci.yml` and but this is easily solved by running inji in
a [git pre-commit hook](https://git-scm.com/docs/githooks#_pre_commit).

