Skip to main content

Jinja2 template linter with multi-ecosystem filter awareness (Salt, Ansible, Home Assistant)

Project description

jinja-multilint

A Jinja2 template linter with ecosystem-aware filter and test name validation.

Built-in support for Salt, Ansible, and Home Assistant. Extensible via plugins for any other Jinja2-based ecosystem, internal organization, or custom filter library.

Why

jinja-multilint fills a specific gap: lightweight, ecosystem-aware validation of Jinja2 filter and test names, without requiring the full runtime of the target ecosystem.

It is not a replacement for ecosystem-specialized linters like ansible-lint or for full configuration validators like Home Assistant's check_config. See Related tools for honest positioning of when to use this versus alternatives.

Install

pip install jinja-multilint

Requires Python 3.10+.

Quick start

Lint a Salt template:

jinja-multilint --ecosystem salt template.sls

With a config file:

# .jinja-multilint.yaml
ecosystems:
  - salt
filters:
  - my_custom_filter
extensions:
  - jinja2.ext.do
jinja-multilint template.sls

Modes

--mode strict   # unknown filter/test → error (default)
--mode warn     # unknown filter/test → stderr warning, exit 0
--mode lax      # skip filter/test checks; only validate syntax

Syntax errors always fail regardless of mode.

Configuration

Auto-detected files in current working directory (first match wins):

  1. .jinja-multilint.yaml or .jinja-multilint.yml
  2. .jinja-multilint-filters (flat list, one filter name per line; legacy compatibility for migrating from drm/jinja2-lint setups)

Override with --config PATH.

Full YAML schema:

mode: strict                    # strict | warn | lax (default: strict)

ecosystems:                     # built-in or third-party plugin names
  - salt
  - ansible
  - my_org                      # third-party, see Plugins below

filters:                        # additional custom filter names
  - my_filter_one
  - my_filter_two

tests:                          # additional custom test names
  - my_test_one

extensions:                     # Jinja2 extension import paths
  - jinja2.ext.do
  - jinja2.ext.loopcontrols

CLI flags extend (do not replace) config values.

CI/CD usage

GitLab CI

lint:jinja:
  image: python:3.12
  script:
    - pip install jinja-multilint
    - find . -type f -name "*.sls" -exec jinja-multilint --ecosystem salt {} +

Bitbucket Pipelines

- step:
    name: 'Lint-Jinja'
    image: python:3.12
    script:
      - pip install jinja-multilint
      - find . -type f -name "*.sls" -exec jinja-multilint --ecosystem salt {} +

GitHub Actions

- name: Lint Jinja templates
  run: |
    pip install jinja-multilint
    find . -type f -name "*.sls" -exec jinja-multilint --ecosystem salt {} +

With a third-party plugin from PyPI

script:
  - pip install jinja-multilint jinja-multilint-my-org
  - find . -type f -name "*.j2" -exec jinja-multilint --ecosystem my_org {} +

With an internal plugin from a private package registry

script:
  - pip install --extra-index-url $PRIVATE_INDEX_URL jinja-multilint jinja-multilint-internal
  - jinja-multilint --ecosystem salt --ecosystem internal templates/

With an in-repo plugin

script:
  - pip install jinja-multilint
  - pip install -e ./tooling/my-jinja-plugin
  - jinja-multilint --ecosystem my_thing templates/

Pre-commit

# .pre-commit-config.yaml
- repo: local
  hooks:
    - id: jinja-multilint
      name: Lint Jinja templates
      entry: jinja-multilint --ecosystem salt
      language: python
      additional_dependencies: [jinja-multilint]
      files: '\.(sls|jinja|j2)$'

Plugins

A plugin is a Python package that registers an ecosystem under the jinja_multilint.ecosystems entry-point group. The plugin module exposes:

  • FILTERS — iterable of Jinja filter name strings
  • TESTS — iterable of Jinja test name strings (used with is)
  • EXTENSIONS — iterable of Jinja2 extension import paths (optional)

Minimal plugin example

A plugin package called jinja-multilint-myorg:

jinja-multilint-myorg/
├── pyproject.toml
└── jml_myorg/
    └── __init__.py

pyproject.toml:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "jinja-multilint-myorg"
version = "0.1.0"

[project.entry-points."jinja_multilint.ecosystems"]
myorg = "jml_myorg"

jml_myorg/__init__.py:

FILTERS = frozenset({
    "myorg_custom_filter",
    "myorg_another_filter",
})

TESTS = frozenset({
    "myorg_test",
})

EXTENSIONS = []  # optional; defaults to empty

After pip install ./jinja-multilint-myorg, users reference the new ecosystem by name (the entry-point key) in config or CLI:

ecosystems:
  - myorg

Plugins can ship any subset of the three exports. A plugin that only defines custom tests can omit FILTERS and EXTENSIONS.

What's in the box

Plugin name Source
salt Salt's documented Jinja extensions
ansible Ansible core filter/test set (no collections)
home_assistant Home Assistant template filters/tests

Scope

jinja-multilint validates:

  • Template syntax (via Jinja2's parser)
  • Filter and test names against the configured allow-list / ecosystem plugins

It does NOT validate:

  • Filter or test argument count or names
  • Filter behavior or return types

Schema-aware filter signature validation may be added as opt-in plugin metadata in a future release. The plugin API is forward-compatible with this change — existing plugins will continue to work.

Related tools

jinja-multilint occupies a specific niche. Other tools cover overlapping or complementary ground; in many cases you should use those instead of (or in addition to) this one.

Style linters (complementary; run alongside)

  • aristanetworks/j2lint (Apache 2.0): Jinja2 style and formatting rules — spaces around operators, variable naming conventions, statement indentation. Complements jinja-multilint: it checks how templates look, this checks whether filter and test names are valid. Both can run in the same CI pipeline.

Ecosystem-specialized linters / validators

When you're working exclusively within one ecosystem, the specialized tool is usually more thorough than jinja-multilint for that ecosystem and should be preferred (or used alongside).

Tool License Covers When to prefer it
ansible-lint GPL-3.0+ Ansible playbooks/roles/tasks — YAML, idioms, FQCN, module argument validation, and embedded Jinja2 with Ansible's filter set Pure Ansible project. Strictly more thorough than us for Ansible content.
salt-lint MIT Salt state file YAML, common antipatterns, SLS structure Salt projects — but salt-lint does not validate Jinja2 filter names, so run it alongside jinja-multilint, not instead.
Home Assistant check_config Apache 2.0 Full HA config validation including template rendering against live state You have HA running locally and want true render-time validation. Not suitable for lightweight CI without an HA install.
dbt parse / dbt compile Apache 2.0 dbt model SQL + Jinja templates Pure dbt project with dbt installed and a profile configured.
drm/jinja2-lint DBAD Generic Jinja2 syntax + user-supplied filter list The original syntax checker that inspired this project. DBAD license makes it awkward for downstream redistribution; no ecosystem knowledge built-in.

Decision guide

Use a specialized linter when:

  • Your repo targets one ecosystem (just Ansible, just HA, just dbt).
  • You need ecosystem-specific rules beyond filter name validation (idiomatic patterns, module argument checks, naming conventions).
  • The specialized tool can run in your environment without overhead concerns.

Use jinja-multilint when:

  • You work in Salt — salt-lint doesn't validate Jinja2 filter names, and this tool fills that gap.
  • Your repo spans multiple ecosystems (e.g., Salt states plus some Ansible plus custom Jinja2 templates).
  • You need lightweight CI checks without pulling in the full Ansible / HA / dbt runtime — pip install jinja-multilint and you're done.
  • You have organization-specific custom filters and want allow-list validation without the heavyweight ecosystem tools' opinionated overhead.
  • You need to write a plugin for a custom Jinja2 application that isn't covered by any specialized tool.

Use them together when:

  • You have a Salt project: salt-lint for SLS structure, jinja-multilint for filter names, aristanetworks/j2lint for Jinja style.
  • You have an Ansible project but also custom Jinja2 templates outside Ansible: ansible-lint for the Ansible bits, jinja-multilint for the custom ones.

License

MPL 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jinja_multilint-0.1.2.tar.gz (19.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jinja_multilint-0.1.2-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file jinja_multilint-0.1.2.tar.gz.

File metadata

  • Download URL: jinja_multilint-0.1.2.tar.gz
  • Upload date:
  • Size: 19.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for jinja_multilint-0.1.2.tar.gz
Algorithm Hash digest
SHA256 30cd2c082ce937d793a8fb2d2c2d694c485bb7ae7dda660cde610c45dd9fecaa
MD5 a6ac378a0d7207ed0f3ae4380c97b577
BLAKE2b-256 22b718cecd97d1fac0775470808bdc09e567f64662df617d8e194d6825fe84e1

See more details on using hashes here.

File details

Details for the file jinja_multilint-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: jinja_multilint-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 20.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for jinja_multilint-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 bd77f7f5100f1e95245ef231782b29557faaf18137aea54bf75e62f4c9ee551f
MD5 962b393c6c9160d0ccbcf9ccefc886e7
BLAKE2b-256 5dfe7d6886472347ef17c8788927b55a39d028f83691f016b84f41ed638aee97

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page