Feedback wanted: ansible-static-lint, static Ansible linting without the Ansible runtime

Hi,

Felix suggested I start a dedicated topic after I mentioned this project in the introduction thread, so here it is :slightly_smiling_face:

I recently built ansible-static-lint (astl), a Go implementation of the ansible-lint rules that can be decided from the YAML source alone.

The starting observation was fairly simple: a significant part of the latency of ansible-lint comes from starting and loading the Ansible runtime, while many lint rules do not actually need that runtime.

So instead of trying to replace ansible-lint, I separated the two concerns:

  • astl handles the static rules where very fast feedback is useful
  • ansible-lint remains the reference for syntax checking, collection resolution, schemas, --fix, and the other runtime-dependent checks

Currently astl implements 38 of the 51 default ansible-lint rules.

On the frozen ansible-lint compatibility corpus, it reproduces 2370 / 2370 findings within its supported scope, including file, line, column and message.

On my test machine:

ansible-lint astl
6-line playbook 2.1 s 2.5 ms
478 files corpus 46.8 s 37 ms

The comparison is intentionally not presented as equivalent work: ansible-lint also performs runtime-dependent checks that astl deliberately does not. The point is that the static subset becomes cheap enough to run in an editor, pre-commit hook, or on every CI push.

I have also tried to make adoption incremental rather than introducing another configuration:

  • existing .ansible-lint configuration
  • skip_list, enable_list, warn_list, profiles
  • # noqa
  • .ansible-lint-ignore (added in v0.2.0 after the first user request)
  • ansible-lint rule identifiers by default
  • GitHub Action and pre-commit support
  • SARIF output

GitHub:

The project is still very young, and at this stage what I am most interested in is real Ansible repositories and workflows where this approach breaks down or is simply not useful.

In particular I’d be interested in hearing:

  • whether a fast static layer alongside ansible-lint makes sense in your workflow
  • which compatibility gaps would prevent you from using it
  • and examples of repositories where I should test it
5 Likes

@arhuman this is great work, and the static/runtime split is the right framing. I want to raise a use case that I think astl is well positioned to solve, and float an idea for it.

The developers I work with write Ansible YAML, not Python, and many of them are on locked down corporate Windows machines. Today they effectively can’t lint in their editor. ansible-lint needs the Python and Ansible runtime, and since the control node isn’t supported natively on Windows, they’re pushed to WSL, Docker/Podman, or installing extra tooling which is blocked or heavily gated in enterprise environments. So a lot of people who would benefit from linting get none.

A Go implementation is a big step forward because a compiled binary carries no runtime dependency. But in the most locked down shops, a standalone .exe has its own issues. A new, unknown native executable would likely need elevated permissions and be flagged by Windows Defender (and other similar tools).

The one common runtime already present and approved on those machines is VS Code itself, which ships Node. So the idea is to compile the existing astl static core to WASM and bundle it inside a VS Code extension. As I understand Go’s WASM targets, that’s a recompile of your current code rather than a rewrite. The rule engine stays as is, and the work is mostly at the I/O boundary (WASI/stdin stdout or JS glue) plus a binary size decision (standard Go vs. TinyGo, the latter being tricky with reflection heavy YAML libs).

From an enterprise approval standpoint that packaging is a lot cleaner than an exe. Nothing new executes at the OS level (the .wasm is data loaded by the already vetted VS Code process), so there’s no new signed binary, no child-process behavior for EDR to chase, and a single platform independent VSIX that’s easy to scan and mirror. Ideally published to Open VSX, and fully self contained with no runtime network calls for limited/no connectivity environments. The native CLI still serves everyone who wants it, and the same rule logic powers both.

Is a WASM/editor target something you’ve considered, or is CLI the intended scope for now?

2 Likes

Thanks Jeff, this is a really interesting use case, and one I hadn’t considered in that form.

I agree that the static core makes a WASM/editor target plausible without changing the rule engine itself.

I’m curious about two points though.

In the corporate environments you have in mind, are VS Code extensions actually easier to get approved than a standalone executable? “astl” itself doesn’t require elevated privileges and only reads the files it analyzes, so I assume the real constraint is more about execution policies / EDR blocking unknown executables than privileges themselves.

I’m also wondering about the filesystem side. “astl” currently operates directly on files and configuration in the workspace. With WASM sandboxing, I assume the VS Code host would need to expose that filesystem access or feed the relevant contents to the WASM module. Have you experimented with this pattern in VS Code?

If a self-contained VSIX is significantly easier to deploy in those environments, and the filesystem boundary remains reasonably simple, then I agree this could be a very interesting target for “astl”.

Thanks for raising it!

One thing to note is that ansible-static-lint is also a lot more secure to run on not 100% trusted collections, since it doesn’t execute code from that collection (see for example #2681 or #4643).

1 Like

This project looks promising! :+1:

Especially the fact that it does not execute ansible-playbook --syntax-check which causes issues with files encrypted by ansible-vault.
It will probably also not fail when used Collections are not installed. (which is annoying in CI environments)

1 Like