Doolta
EN · FR Write to us

Doolta

ansible-static-lint

GitHub →

Ansible linting went from a minute to milliseconds the day the runtime was removed: 38 ansible-lint rules reimplemented in Go, byte-for-byte identical output.

ansible-static-lint

Project Details

  • The problemAnsible linting drags CI processing time down: 46.8 s to pass a 478-file corpus, 2.1 s for a 6-line playbook. On every push, in pre-commit, or on editor save, that wait breaks the feedback loop.
  • The analysisThe cost is not in the rules, it is in launching Ansible: the Python interpreter, imports, collection resolution, a syntax-check subprocess, all before the first check runs. Even ansible-lint --version costs half a second. A rule itself is just a predicate over an already-parsed document: its marginal cost is measured in microseconds.
  • The intuitionWhat if we only did the static analysis? Most rules can be decided from the YAML source alone, with no Ansible runtime. A binary that parses YAML and applies predicates only pays for startup, I/O and parsing: the number of rules becomes almost free.
  • The smart executionSorting ansible-lint’s 51 default rules: 38 are statically decidable and reimplemented in Go, the 13 that need the runtime stay with the original. Within that scope, compatibility is total: pep8 output reproduced byte for byte, .ansible-lint config and noqa comments unchanged, a contract frozen on the ansible-lint 26.8.0 corpus. Result: from a minute to milliseconds (37 ms on the corpus, 2.2 ms cold start), with make bench failing the build past 150 ms.
  • Associated offerBuilds on the DevOps Sprint offer: shortening CI feedback loops and hardening tooling.

A pipeline that waits 47 seconds for its linter has a problem, and that problem is not the linting: it is launching Ansible. astl starts from that diagnosis and follows it all the way: if the runtime is the cost, remove the runtime, and keep every rule that never needed it. 38 of ansible-lint’s 51 default rules, reimplemented in Go, delivered in milliseconds, with the same output byte for byte.

The winning pipeline runs both: astl on every push for the rules that block most CI runs, ansible-lint nightly for the 13 rules that need the runtime. This project demonstrates a directly transferable capability: finding where the time actually goes, deriving an architecture from it, and locking the result down with a compatibility contract and a performance guard.