Skip to content

ci: add Lua lint gate with luacheck #98

@membphis

Description

@membphis

Background

qjson has strong Rust-side quality gates today: release-mode tests, scalar-only tests, panic-barrier tests, fuzzing, Miri/ASan coverage, cargo-audit, Lua busted integration tests, and LuaRocks package validation.

The Lua side currently does not have an equivalent static analysis gate. The public Lua wrapper and lazy table/encoder logic live in:

  • lua/qjson.lua
  • lua/qjson/lib.lua
  • lua/qjson/table.lua

The test suite also contains non-trivial Lua helpers and mocks under tests/lua/. Runtime tests cover behavior, but they do not reliably catch all accidental globals, misspelled locals, unused variables, duplicate definitions, or low-level mistakes in rarely exercised branches.

Kong/lua-resty-simdjson uses luacheck as a lightweight Lua static analysis gate. qjson should add a similarly scoped gate, without turning this into a formatting or style-enforcement project.

The lint workflow should be local-first: developers and AI coding agents need a quick local command that finds Lua lint problems before pushing. CI should run the same command as final enforcement, rather than hiding the lint logic only inside GitHub Actions.

Goal

Add a lightweight Lua lint quality gate that catches accidental globals and common Lua mistakes in both production Lua code and Lua tests, with an easy local entrypoint and CI enforcement using that same entrypoint.

Proposed Scope

Add luacheck coverage for:

  • lua/
  • tests/lua/

The initial implementation should include:

  • A repository .luacheckrc configured for LuaJIT-oriented code.
  • Allowed globals for the busted test environment, such as describe, it, assert, before_each, after_each, if needed.
  • Any explicit allowances required for LuaJIT FFI idioms used by this project.
  • A local Makefile target, for example make lua-lint, that runs the Lua lint check directly.
  • The target should be listed by make help and should fail clearly when luacheck is missing.
  • A GitHub Actions lint step/job that installs luacheck and runs the local target, rather than duplicating the raw lint command inline.

A reasonable local command shape is:

make lua-lint

The underlying check should be equivalent to:

luacheck lua tests/lua

The configuration should be strict enough to catch meaningful mistakes, but conservative enough to avoid large unrelated churn.

Non-Goals

  • Do not introduce a Lua formatter such as stylua.
  • Do not add formatting or whitespace-only requirements.
  • Do not refactor Lua code for style reasons.
  • Do not change Rust formatting policy or add cargo fmt --check.
  • Do not require the first implementation to fold Lua lint into make lint. It is acceptable to keep make lint as Rust clippy-only and add a separate make lua-lint target. If make lint is changed to include Lua lint, update AGENTS.md, CONTRIBUTING.md, and any other command documentation in the same PR.
  • Do not use a changed-files-only lint strategy in the first version. The Lua codebase is small enough that full linting is simpler and gives clearer local and CI behavior.

Acceptance Criteria

  • The repository has a .luacheckrc checked in.
  • make lua-lint works locally and runs Lua lint over lua/ and tests/lua/.
  • make help lists the Lua lint target with a clear description.
  • If luacheck is not installed locally, the failure mode is clear enough for a developer or AI agent to install the missing tool and retry.
  • CI runs the same local lint entrypoint on pull requests.
  • The lint job fails on accidental global writes or undefined globals in project Lua files, except for explicitly allowed test/runtime globals.
  • The lint configuration supports LuaJIT FFI usage in the existing codebase.
  • The initial PR either passes with minimal code cleanup or documents any narrowly scoped suppressions in .luacheckrc.
  • The change does not introduce a Lua formatting gate.
  • The change does not require broad style-only rewrites.

Implementation Notes

A reasonable first pass is:

  1. Add .luacheckrc with LuaJIT/busted-aware settings.
  2. Add make lua-lint as the local developer entrypoint.
  3. Run make lua-lint locally and inspect warnings.
  4. Prefer fixing real issues over suppressing them.
  5. Suppress only warnings that are intentional for this project, and keep suppressions specific.
  6. Add a CI job or step that installs luacheck through LuaRocks and runs make lua-lint.

Keep the local target simple and readable. It should be easy for a contributor or AI coding agent to run, see failures, fix the Lua files, and rerun without needing to reproduce the full CI environment.

Follow-Ups

Potential future improvements, intentionally excluded from the first PR:

  • Fold lua-lint into make lint if the project later wants make lint to represent all language linters.
  • Add changed-files optimization if the Lua codebase grows substantially.
  • Consider stricter warning categories after the initial gate has been stable for a while.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions