Skip to content

feat: add substitution-key rule - #64

Open
shreyaGupta1202 wants to merge 3 commits into
envaar:mainfrom
shreyaGupta1202:feat/lint-substitution-key
Open

shreyaGupta1202 wants to merge 3 commits into
envaar:mainfrom
shreyaGupta1202:feat/lint-substitution-key

Conversation

@shreyaGupta1202

@shreyaGupta1202 shreyaGupta1202 commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #46. Add the deterministic substitution-key lint rule for malformed $KEY and ${KEY} syntax in dotenv assignment values.

Why

Malformed substitution syntax can be interpreted differently across dotenv parsers, shells and deployment tools. Vaar should report incomplete, empty or mismatched substitution syntax before the file reaches an application or deployment environment.

Type

  • Bug fix
  • New rule
  • Parser change/fix
  • Reporter change/fix
  • Documentation
  • CI / Release
  • Build
  • Performance improvement/change
  • Internal refactor
  • Hotfix

Breaking change

  • No
  • Yes (describe required migration)

Changes

  • Added the substitution-key deterministic lint rule and registered it in the canonical rule set.
  • Added coverage for valid substitutions, malformed substitutions, quoted values, inline comments, unrelated braces, --only, --skip and fixability behavior.

User-visible changes

Before:

ABC=${BAR
FOO="$BAR}"
EMPTY_REFERENCE=${}

These malformed substitution values were not reported by a dedicated rule.

After:

error substitution-key .env:1 substitution "${BAR" is missing a closing "}"
error substitution-key .env:2 substitution "$BAR}" contains an unmatched closing "}"
error substitution-key .env:3 substitution "${}" is empty

Validation

Commands run:

  • gofmt -w <touched Go files>
  • make lint
  • go test ./...
  • go run ./cmd/vaar lint ...

Additional commands:

env GOCACHE=/tmp/go-build go test ./internal/lint/rules/deterministic ./internal/lint/rules ./internal/cli
env GOCACHE=/tmp/go-build go test ./...

Tests

  • Added tests
  • Updated existing tests
  • No tests needed (explain)

Documentation

  • Updated documentation
  • Not applicable

Release notes

Add the substitution-key lint rule to report malformed $KEY and ${KEY} substitution syntax in dotenv assignment values.

Reviewer notes

Please pay special attention to substitution parsing scope: the rule scans unquoted and double-quoted assignment values, ignores recognized inline comments, treats single-quoted values as literal text, and does not expand values or check whether referenced keys exist.

Checklist

  • PR title follows Conventional Commits
  • Code is formatted
  • Tests pass
  • Documentation is updated (if needed)
  • Release note added (if needed)
  • No real secrets, credentials or sensitive data is included

Summary by CodeRabbit

  • New Features
    • Added the substitution-key lint rule to detect malformed $KEY / ${KEY} syntax in dotenv assignment values.
    • Reports missing closing braces, empty ${}, and unmatched extra } with precise, line-based errors.
    • Works with --only / --skip and skips scanning inside single-quoted values.
  • Documentation
    • Updated the rule catalog and added a dedicated rule page with good/bad examples and sample CLI output.
  • Tests
    • Added unit and end-to-end CLI coverage for rule selection, reporting, and fixability coverage.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds the deterministic substitution-key lint rule, registers it in the rule set, validates malformed $KEY and ${KEY} syntax, adds CLI and unit coverage, and documents the reporting-only behavior.

Changes

Substitution-key lint rule

Layer / File(s) Summary
Rule scanning and unit coverage
internal/lint/rules/deterministic/substitution_key.go, internal/lint/rules/deterministic/substitution_key_test.go
Scans dotenv assignment values for malformed substitutions, handles quoted values and portable key names, and tests valid, invalid, repeated, and skipped patterns.
Rule registry and CLI integration
internal/lint/rules/all.go, internal/lint/rules/compat.go, internal/cli/lint_test.go, internal/lint/rules/fixability_test.go
Registers substitution-key, exposes its constructor, verifies --only and --skip, and adds fixability coverage.
Rule catalog and reference documentation
docs/lint/rules/README.md, docs/lint/rules/deterministic/substitution-key.md
Adds the catalog entry and documents detection behavior, exclusions, manual fixes, examples, and CLI output.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant lintCLI
  participant rulesAll
  participant substitutionKeyRule
  lintCLI->>rulesAll: Load registered lint rules
  rulesAll-->>lintCLI: Return substitutionKeyRule
  lintCLI->>substitutionKeyRule: Apply rule selection and lint values
  substitutionKeyRule-->>lintCLI: Return malformed-substitution findings
Loading

Possibly related PRs

  • envaar/vaar#30: Updates the lint rule interface used by the new rule’s Description() metadata.
  • envaar/vaar#45: Introduces the fixability framework used by this rule’s fixture coverage.

Suggested reviewers: copilot, nitjsefnie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately describes the primary change: adding the substitution-key rule.
Linked Issues check ✅ Passed The PR implements the new rule, docs, registry entry, CLI selection, and tests required by issue #46.
Out of Scope Changes check ✅ Passed The changes are limited to the new rule, its docs, registry wiring, and tests, with no clear unrelated additions.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/lint/rules/deterministic/substitution_key.go`:
- Around line 145-153: Ignore unsupported braced expansions in the
substitution-key rule: update the validation around validKeyName so only $KEY
and ${KEY} forms are evaluated, while forms such as ${KEY:-fallback} return no
finding. In internal/lint/rules/deterministic/substitution_key_test.go lines
43-66, replace the invalid-key expectation with cases confirming unsupported
expansions are ignored. In docs/lint/rules/deterministic/substitution-key.md
lines 18-21, remove the claim that non-portable braced names are reported.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3033e85b-20a3-4e9a-8d7a-964ead6d57db

📥 Commits

Reviewing files that changed from the base of the PR and between e34569f and 2951bad.

📒 Files selected for processing (8)
  • docs/lint/rules/README.md
  • docs/lint/rules/deterministic/substitution-key.md
  • internal/cli/lint_test.go
  • internal/lint/rules/all.go
  • internal/lint/rules/compat.go
  • internal/lint/rules/deterministic/substitution_key.go
  • internal/lint/rules/deterministic/substitution_key_test.go
  • internal/lint/rules/fixability_test.go

Comment thread internal/lint/rules/deterministic/substitution_key.go Outdated
@shreyaGupta1202

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit full review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 33 minutes.

@TFM-HITESH
TFM-HITESH requested a review from Copilot July 29, 2026 15:47
@TFM-HITESH

Copy link
Copy Markdown
Contributor

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new deterministic lint rule (substitution-key) to Vaar’s dotenv linter to detect malformed $KEY / ${KEY} substitution syntax inside assignment values, addressing #46 and reducing parser-to-parser ambiguity before deployment/runtime.

Changes:

  • Implemented substitution-key rule to report missing closing }, unmatched extra }, and empty ${} (while skipping single-quoted and unbalanced-quote values).
  • Registered the rule in the canonical rule set and compatibility constructors; added fixtures and CLI tests for --only / --skip.
  • Documented the new rule in the lint rule index and added a dedicated rule page with examples and sample output.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
internal/lint/rules/fixability_test.go Adds a fixture for substitution-key to ensure fixability behavior stays pinned.
internal/lint/rules/deterministic/substitution_key.go Implements the deterministic substitution-key rule and scanning helpers.
internal/lint/rules/deterministic/substitution_key_test.go Adds unit tests covering valid/invalid substitutions, quoting, comments, and multiple findings.
internal/lint/rules/compat.go Exposes the new rule via the compat constructor.
internal/lint/rules/all.go Registers substitution-key in the canonical rule list.
internal/cli/lint_test.go Adds end-to-end CLI tests for --only=substitution-key and --skip=substitution-key.
docs/lint/rules/README.md Adds substitution-key to the rule catalog table.
docs/lint/rules/deterministic/substitution-key.md Provides full rule documentation with examples and expected output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/lint/rules/deterministic/substitution_key.go`:
- Around line 121-130: Update the missing-closing-brace branch in the
substitution key rule to detect and ignore non-empty unsupported braced
expansion candidates such as `${KEY:-fallback` before creating the lint finding,
while preserving errors for genuinely malformed supported substitutions. Add a
test covering this ignored expansion behavior in the existing substitution-key
ignored-expansion test suite.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9908c5f8-bfdb-4623-964e-f4949c05455a

📥 Commits

Reviewing files that changed from the base of the PR and between e34569f and 2f71c46.

📒 Files selected for processing (8)
  • docs/lint/rules/README.md
  • docs/lint/rules/deterministic/substitution-key.md
  • internal/cli/lint_test.go
  • internal/lint/rules/all.go
  • internal/lint/rules/compat.go
  • internal/lint/rules/deterministic/substitution_key.go
  • internal/lint/rules/deterministic/substitution_key_test.go
  • internal/lint/rules/fixability_test.go

Comment on lines +121 to +130
closeOffset := strings.IndexByte(value[start+2:], '}')
if closeOffset < 0 {
finding := finding(
substitutionKeyRule{}.ID(),
lint.SeverityError,
path,
line,
fmt.Sprintf(`substitution %q is missing a closing "}"`, value[start:]),
)
return &finding, len(value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Ignore unclosed unsupported braced expansions.

${KEY:-fallback reaches this missing-brace branch and is reported, despite unsupported expansion syntax being out of scope. Classify a non-empty invalid candidate before emitting the finding, and add this case to the ignored-expansion tests.

Proposed fix
 if closeOffset < 0 {
+    candidate := value[start+2:]
+    if candidate != "" && !validKeyName(candidate) {
+        return nil, len(value)
+    }
     finding := finding(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/lint/rules/deterministic/substitution_key.go` around lines 121 -
130, Update the missing-closing-brace branch in the substitution key rule to
detect and ignore non-empty unsupported braced expansion candidates such as
`${KEY:-fallback` before creating the lint finding, while preserving errors for
genuinely malformed supported substitutions. Add a test covering this ignored
expansion behavior in the existing substitution-key ignored-expansion test
suite.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rule: add substitution-key

3 participants