Introduction
iglint is a linter for Inductive Automation Ignition 8.3 projects, written in Rust by Mustry Solutions. It reads a project the way the gateway stores it on disk (the projects/ folder of a gateway data directory or a project repository) and reports problems before they reach a gateway:
- Perspective views: the component tree, properties, bindings, transforms, event actions, message handlers and custom methods.
- Jython 2.7 scripts, everywhere they live: the project library, gateway and session events, WebDev handlers, tag event scripts, and the scripts embedded in
view.jsonfiles. A finding inside an embedded script points inside the JSON string. - Expressions in bindings, transforms, tags and named query parameters.
- References between resources: views, named queries, library functions, message types, style classes, page URLs and parent projects.
- Tags and gateway resources: UDT types and parameters, tag paths, alarm bindings and history providers.
It has 110 rules in ten categories (see the rules overview), safe and unsafe auto-fixes, a baseline for adopting it on existing projects, output formats for every common CI system, a language server for editors, and a compatibility mode for projects that use ign-lint.
Why iglint
Section titled “Why iglint”It understands Jython 2.7 exactly. Ignition runs scripts on its own fork of Jython 2.7. Generic Python linters assume Python 3 (or CPython 2.7), so they report names Ignition provides (system, the project's library packages) and miss errors that only Jython's compiler raises. iglint's parser accepts what Ignition's Jython accepts and produces the same syntax tree; it is tested against the Jython inside the inductiveautomation/ignition:8.3.3 Docker image. The compiler checks (SCR002, SCR003), name resolution and string formatting rules are verified against the same Jython. See How iglint is validated.
It knows Ignition. The system.* scripting API, the expression functions and the Perspective component schemas were generated by introspecting a real 8.3 gateway. iglint knows which names Ignition injects into each kind of script, which header Ignition wraps around a Perspective script, and which system.* functions exist in which scope.
It looks at the whole project. Rules that cross resources (an unknown view in openPopup, a named query that does not exist, a library function called with the wrong number of arguments, a message type no handler listens for) use an index of every project in the workspace, including inherited projects and the gateway configuration.
A false positive is a bug. When a rule cannot decide with certainty (a computed view path, a tag path built at run time, a module that rewrites its own namespace), it stays silent, or reports only in strict mode. The limits are listed honestly in What iglint does not catch.
It is fast. Each file is read and parsed once, files are processed in parallel and results are cached. On the synthetic benchmark corpora, iglint does the same work as ign-lint 7x to 53x faster on a 16-CPU machine (with and without pylint), and its own default rule set, which covers far more than ign-lint, runs a 500-view corpus in about 0.16 s. See Performance and caching.
iglint and ign-lint
Section titled “iglint and ign-lint”ign-lint (bw-design-group/ignition-lint, version 0.6.4) is the linter iglint was built to replace. It checks view.json files one at a time, with nine rules, and runs pylint over the scripts of each view.
| ign-lint 0.6.4 | iglint | |
|---|---|---|
| Files linted | view.json only |
views, library modules, event scripts, WebDev, named queries, style classes, page configuration, session properties, tags and UDTs |
| Rules | 9 | 110, including an equivalent for each of the 9 |
| Script analysis | pylint (Python 3 semantics) on a combined file per view | a Jython 2.7 parser, semantic model and compiler checks per script, plus optional real pylint (SCR900) |
| Cross-resource checks | none | views, library members and arity, named queries and parameters, messages, popups, page URLs, style classes, parent projects, UDTs, tags |
| Auto-fix | yes | yes, split into safe and unsafe fixes |
| Configuration | rule_config.json, whitelist |
iglint.toml or [tool.iglint]; reads rule_config.json and whitelists too |
| Adoption on an existing project | whitelist (skips whole files) | baseline (records today's findings, new ones fail) |
| Output | its own format | text, JSON, SARIF, GitHub, GitLab, Azure DevOps, JUnit, pylint and more; ign-lint's own format in iglint ign-lint |
| Editor support | none | language server and VS Code extension |
Parity is tested, not claimed: a test compares iglint ign-lint with output recorded from real ign-lint 0.6.4 on every fixture view and configuration variant, and every intended difference is listed with its reason in Differences from ign-lint. Most differences are places where ign-lint is wrong (it aborts the whole run on one invalid JSON file, it matches custom property references as substrings, so {view.custom.foobar} counts as a use of foo, and it crashes on deeply nested views). Because iglint finds more, a CI gate that passes with ign-lint can fail after switching; the migration guide shows how to switch without breaking the build.
What iglint is not
Section titled “What iglint is not”- It does not run your project. It reads the files in the repository and knows nothing about the gateway's runtime state, the values in tags or databases, or code it cannot see. See What iglint does not catch.
- It does not lint Vision windows and templates (it identifies them, but does not read them). Vision client event scripts (
.pyfiles) are linted. - It targets Ignition 8.3: the resource layout, the scripting API, the expression functions and the component schemas it checks against are 8.3's. Other versions are not tested.
- It is not affiliated with or endorsed by Inductive Automation.
Next steps
Section titled “Next steps”- Install iglint.
- Follow the 5-minute tutorial on a small example project.
- Browse what iglint catches, with examples.