Skip to content

arkenv lint #481

Description

@yamcodes

Summary

arkenv lint is a dev-time linter for .env* files on disk. It is complementary to arkenv check, not a second name for the same command.

  • check (#962, shipped in #1632): would this process boot? Subject is the resolved environment (process.env plus optional --env-file overlays) vs the schema. Key-level findings. CI / hooks.
  • lint (this issue): are these dotenv files well-authored? Subject is files on disk: syntax, duplicates, unquoted spaces, git-tracked secrets, example placeholders, and schema issues pinned to file:line:col. Editor problem matchers and pre-commit on .env*.
  • sync (#1234): writes files from the schema. Third sibling, same schema loader.

Do not fold lint into check --lint / --format unix. --env-file on check means “overlay into the resolved env,” not “lint these files.” Exit bands differ (check findings are exit 4; lint violations should fail a file-oriented run). CI must not pick up editor-only rules by accident.

Parser / coordinate-mapping strategy: ADR 0017.


How they build on each other

One pipeline, two subjects:

schema load (CLI schema loader)
        ↓
env record  ──►  validate()  ──►  EnvIssue[]
        ↑
   dotenv parse

lint must reuse check’s schema loader and validate() — not fork a second validation path. It grows the dotenv parser so a parse yields both an env record and a coordinate map, runs static rules that only make sense on files, then remaps EnvIssue[] onto those coordinates.

Today’s parseDotenv already feeds check but drops coordinates and skips malformed lines. That is the stub lint replaces, not a second parser.


Status quo

arkenv check validates the resolved environment against the schema (--schema, repeatable --env-file, --json / --agent, exit 0 / 4 / 2). Diagnostics hang off the schema path. There is no command that treats .env* as source artifacts, no Unix path:line:col output, no cascade-by-mode, and no static dotenv rules.


Desired behavior

Command

arkenv lint [--schema <path>] [--env <name>]

Global flags (--quiet, --json, --agent, --help) apply as on other CLI commands.

Input

  • Discover the schema the same way check does (package.json arkenv entry, convention paths, --schema override).

  • Resolve dotenv files in standard cascade order for --env <name> (default process.env.NODE_ENV or development):

    .env.[mode].local > .env.[mode] > .env.local > .env

    Skip .env.local when mode === "test".

Parser

Coordinate-aware line scanner (ADR 0017): each defined key maps to file, line, and col. Malformed lines are diagnostics, not silent skips.

Validation

Run the same schema validate() as check against the cascaded record. Map each EnvIssue back to the winning file/line for that key (or a sensible “missing” location when the key is absent).

Output

  • Default: Unix problem-matcher format path/to/file.env:line:col: message
  • --json / --agent: settlement envelope; diagnostics include where: { path, line, column } pointing at dotenv files, not the schema module

Exit codes

  • 0: no syntax, static, or schema violations
  • Non-zero on any lint or validation finding (file-oriented; do not reuse check’s exit 4 “completed with findings” band unless a later protocol ADR says otherwise)

Lint rules

  • Rule 1 (Schema validation): values vs the imported schema (types, missing keys, bounds, patterns), mapped to coordinates.
  • Rule 2 (Syntax): malformed lines (e.g. missing =).
  • Rule 3 (Unquoted spaces): warn on unquoted values that contain spaces.
  • Rule 4 (Git): warn if sensitive .env files are tracked instead of ignored.
  • Rule 5 (Duplicates): error if a key is defined more than once in the same file.
  • Rule 6 (Example placeholders): .env.example must not contain real secrets.

Sub-tasks

Phase 1: Coordinate-aware parser

  • Grow the existing dotenv parser so it keeps file / line / col (and reports syntax instead of skipping)
  • Tests: coordinates, quoting/escaping, syntax errors, comments, multiline quotes

Phase 2: Command and cascade

  • Scaffold arkenv lint (--schema, --env); do not overload check --env-file
  • Reuse schema discovery / load from the CLI schema loader
  • Cascading file resolution (including .env.local skip in test mode)

Phase 3: Safe validation + coordinate mapping

  • validate() on the cascaded record (same path as check)
  • Map EnvIssue[] onto dotenv coordinates

Phase 4: Static rules

  • Rules 2–6 as specified above

Phase 5: Formatting and exit

  • Unix path:line:col default
  • JSON / agent envelope with file where
  • Non-zero exit on any finding

Phase 6: Tests

  • CLI / integration coverage for arkenv lint
  • Confirm arkenv check behavior is unchanged (resolved env, --env-file overlays, exit 4 on schema findings)

Out of scope

  • Auto-fixing .env files
  • SARIF
  • Changing arkenv check’s contract (resolved env, --env-file as overlay, exit 4 on findings)
  • Wrapping dotenv-linter (rejected in ADR 0017)
  • Folding this into flags on check

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or improvementmetaTracks other issues and pull requests

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions