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.1.tar.gz (19.0 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.1-py3-none-any.whl (19.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: jinja_multilint-0.1.1.tar.gz
  • Upload date:
  • Size: 19.0 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.1.tar.gz
Algorithm Hash digest
SHA256 77a1aca27f238d398dd32aebc6a592b6320b3273e030ce485323204b9a1e48c7
MD5 816edfd6667fc6e1aa854bb270d5e95f
BLAKE2b-256 caf2a0dbe791b543838e637b62d73dd4a002263604f5a66618e38b1a7cbed061

See more details on using hashes here.

File details

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

File metadata

  • Download URL: jinja_multilint-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 19.9 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6eda5fd7a57f11c2cf0bb88d4f1803ae2dd5e177e6b96308ae15356b9e5205d5
MD5 28126cba2b7f9472911c1792d8e603eb
BLAKE2b-256 4e9a9c42cc37c75601409dfbfd9a2157d646898ecf1339586bc3c5da9b79b805

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